<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>AI 工程 on 向叔记事簿</title>
        <link>https://ttf248.life/categories/ai-%E5%B7%A5%E7%A8%8B/</link>
        <description>Recent content in AI 工程 on 向叔记事簿</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Thu, 23 Jul 2026 10:52:57 +0800</lastBuildDate><atom:link href="https://ttf248.life/categories/ai-%E5%B7%A5%E7%A8%8B/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Vera Rubin 的十倍每兆瓦 Tokens，会把 AI 价格打下来吗？</title>
        <link>https://ttf248.life/p/vera-rubin-tokens-per-megawatt-ai-price/</link>
        <pubDate>Wed, 22 Jul 2026 21:13:54 +0800</pubDate>
        
        <guid>https://ttf248.life/p/vera-rubin-tokens-per-megawatt-ai-price/</guid>
        <description>&lt;p&gt;按英伟达针对 DeepSeek-R1 推理公布的厂商基准，Vera Rubin NVL72 的每兆瓦 Tokens 指标是其 GB200 NVL72 对比项的十倍。这是机架级系统的比较，不是单颗 GPU 的结论。&lt;/p&gt;
&lt;p&gt;这个数字大得足以让人顺着算出下一句：既然同一兆瓦能做十倍的活，AI 应该很快降价。&lt;/p&gt;
&lt;p&gt;我觉得方向大体是对的，结论却下得太快。&lt;/p&gt;
&lt;p&gt;先把这十倍放回它的测试口径里。它是 NVIDIA 在 DeepSeek-R1 推理工作负载、机架级 NVL72 对机架级 NVL72 的厂商基准。它说的是，在受电力约束的场景里，一兆瓦可以交付多少 Tokens；没有说所有模型都快十倍，也没有说每一百万 Token 的完整成本已经少了九成。公开材料不足以把这项指标换算成现实服务成本：训练、长上下文、低并发、小批量、不同精度和不同服务质量目标，都可能给出另一张表。&lt;/p&gt;
&lt;p&gt;不过，这个指标仍然很重要。在受电力接入、液冷或机房交付限制的项目里，若同一份电力额度能跑出更多推理，扩容不必每次都先等变电站和机房。&lt;/p&gt;
&lt;p&gt;问题是，电费只是成本表里的一行。&lt;/p&gt;
&lt;p&gt;一套 Vera Rubin NVL72 不是把旧机架里的卡换掉就结束了。新 GPU、互连、交换网络、液冷和部署工程会一起进入资本开支。服务商还要面对折旧期、融资成本、软件迁移、故障冗余，还有利用率。机器闲着时，再漂亮的每兆瓦吞吐量也不会自动变成便宜的 Token。&lt;/p&gt;
&lt;p&gt;把服务商的单位服务成本简化为：&lt;/p&gt;
$$
\text{单位 Token 成本} \approx \frac{\text{折旧 + 电力 + 机房网络 + 运维}}{\text{实际售出的 Tokens}}
$$&lt;p&gt;这是解释变量关系的简化框架，不是依据公开材料计算出的 TCO；这里的分母是实际售出的 Tokens，不是设备理论吞吐量。每兆瓦 Tokens 的基准直接改善的是单位电力可支持的推理产能，并可能降低每 Token 的电力投入；只有需求与利用率跟上，才会通过更高的实际售出量改善单位服务成本。新系统的资本开支、折旧和部署成本也可能上升。网络、存储、排队和调度同样可能成为限制因素，系统不是单颗 GPU 在跑。&lt;/p&gt;
&lt;p&gt;先发生的未必是降价。服务商可能先把新增余量用在更长的上下文、更快的响应、更高的并发，或接住原本排队的企业需求；对开发者有价值，价目表却不必马上变化。&lt;/p&gt;
&lt;p&gt;真正的降价要等新机架从样板项目变成可复制的机房配置，利用率也被拉起来。到那时，云厂商、模型公司和推理服务商是否愿意把效率让给客户，还要看竞争。更棘手的是需求：便宜的推理若立刻诱发十倍调用量，行业得到的可能是更大的总支出和更多数据中心，而不是每个人都觉得便宜。&lt;/p&gt;
&lt;p&gt;AI 基础设施会同时制造通缩和扩张。单次生成可能更省电，用户可能得到更好的模型能力，服务商却可能因此建更多机房，因为新能力会拉出新的用法。&lt;/p&gt;
&lt;p&gt;因此，Vera Rubin 的十倍更像一张通往降价的门票，不是降价通知书。它改善了电力受限场景下的推理产能，也给服务商降价留出了空间。空间最后会被谁拿走，要看硬件定价、折旧、机房、利用率和竞争。&lt;/p&gt;
&lt;p&gt;用户最终不必执着于“API 会不会立刻便宜十倍”。更值得观察的是两个更朴素的信号：同一笔预算能否稳定买到更快、更长、更可靠的推理，以及当供给起来后，服务商会把多少效率留在利润表，多少写进价目表。前者可能先以产品能力的形式出现；后者是否出现及出现的时间，仍取决于供给、利用率和竞争。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.nvidia.com/en-us/data-center/vera-rubin/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA Vera Rubin 数据中心平台&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.nvidia.com/en-us/data-center/gb200-nvl72/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA GB200 NVL72&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.nvidia.com/en-us/networking/products/spectrum-x/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA Spectrum-X 网络&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;英伟达 Vera Rubin 全面量产，Spectrum-6 网络与 NVL72 能效跃升 10 倍。DeepSeek R1 推理测试显示，Vera Rubin NVL72 每兆瓦 Tokens 吞吐量较 GB200 NVL72 提升 10 倍。大面积铺货以后，AI 是不是能降价，很明显的能效比提升了，但是硬件投入增加了很多。&lt;/p&gt;
&lt;/blockquote&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>给编码模型几个代码锚点</title>
        <link>https://ttf248.life/p/code-anchors-for-llm-requirements/</link>
        <pubDate>Sun, 05 Jul 2026 23:04:15 +0800</pubDate>
        
        <guid>https://ttf248.life/p/code-anchors-for-llm-requirements/</guid>
        <description>&lt;p&gt;给编码模型写需求时，我越来越倾向于顺手带上几个能在仓库里搜到的名字：函数名、组件名、CSS 类名，或者接口路径。少了这些词，模型未必做不了，但它经常要先花一轮时间猜“这句话对应哪段代码”。&lt;/p&gt;
&lt;p&gt;比如只写：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;调整登录按钮的禁用状态，提交中不要重复点击，样式也要变灰。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;人能借助页面印象理解“登录按钮”在哪，编码模型面对的却可能是几十个按钮、多个登录入口，以及散落在组件、状态管理和样式文件里的实现。它首先要把自然语言中的“登录按钮”映射到仓库里的具体符号，然后才能判断该改事件处理、状态变量，还是 CSS。&lt;/p&gt;
&lt;p&gt;如果需求改成下面这样，搜索空间会小很多：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;调整 &lt;code&gt;LoginForm&lt;/code&gt; 中 &lt;code&gt;handleSubmit&lt;/code&gt; 的提交状态。提交期间禁用 &lt;code&gt;.login-submit&lt;/code&gt;，避免重复请求；现有错误提示行为不变。验收：请求未结束时再次点击不会发出第二个请求，按钮呈禁用样式，请求结束后恢复。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里真正有用的不是提示词变长，而是多了几枚可以落到代码上的锚点。&lt;/p&gt;
&lt;h2 id=&#34;模型在预测代理还要先找代码&#34;&gt;模型在预测，代理还要先找代码
&lt;/h2&gt;&lt;p&gt;当前主流大语言模型通常把输入切成 token，根据已有上下文逐步预测后续 token。Transformer 的自注意力机制让序列中的 token 能彼此建立联系；经过大规模预训练和后续对齐，模型可以遵循指令、解释代码，并生成修改方案。但“能根据上下文生成”不等于“天然知道你当前仓库里登录按钮放在哪”。&lt;/p&gt;
&lt;p&gt;编码代理还多了一层工作：它会调用文件搜索、文本检索、读取和测试工具，把仓库里的相关代码放进模型上下文。函数名 &lt;code&gt;handleSubmit&lt;/code&gt; 和类名 &lt;code&gt;.login-submit&lt;/code&gt; 在这个阶段有双重作用。&lt;/p&gt;
&lt;p&gt;第一重是&lt;strong&gt;检索锚点&lt;/strong&gt;。自然语言里的“登录按钮”可能匹配文案、注释、测试和多个组件；精确符号可以直接交给文本搜索工具，较快找到定义、引用和相邻测试。少读几个无关文件，不只节省时间和 token，也减少无关代码干扰判断。&lt;/p&gt;
&lt;p&gt;第二重是&lt;strong&gt;语义约束&lt;/strong&gt;。&lt;code&gt;handleSubmit&lt;/code&gt; 暗示改动与提交链路有关，&lt;code&gt;.login-submit&lt;/code&gt; 把视觉状态限定到一个具体选择器，&lt;code&gt;LoginForm&lt;/code&gt; 又给出了组件边界。它们共同降低了“按钮究竟是哪一个、状态应该放在哪一层”的歧义。模型仍在做概率生成，但可选解释少了，下一步更容易沿着正确代码路径展开。&lt;/p&gt;
&lt;p&gt;这也是为什么我更愿意把这种写法叫“提供坐标”，而不是“提示词技巧”。它同时帮助了模型和模型外面的工具链。哪怕换成另一个编码模型，只要它也需要检索仓库，这些坐标仍然有价值。&lt;/p&gt;
&lt;h2 id=&#34;好需求不只是一串关键词&#34;&gt;好需求不只是一串关键词
&lt;/h2&gt;&lt;p&gt;只有符号名也不够。下面这种写法虽然容易搜索，仍然不知道要改成什么：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;LoginForm&lt;/code&gt;、&lt;code&gt;handleSubmit&lt;/code&gt;、&lt;code&gt;.login-submit&lt;/code&gt;，优化一下。&lt;/p&gt;
&lt;/blockquote&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;&lt;code&gt;LoginForm&lt;/code&gt;、&lt;code&gt;handleSubmit&lt;/code&gt;、&lt;code&gt;.login-submit&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&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;保持项&lt;/td&gt;
					&lt;td&gt;防止顺手改坏邻接行为&lt;/td&gt;
					&lt;td&gt;现有错误提示不变&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;验收条件&lt;/td&gt;
					&lt;td&gt;给实现和测试一个终点&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;p&gt;还要警惕错误坐标。函数已经重命名、类名在多处复用，或者需求指向的其实是另一个页面时，精确但错误的关键词会让模型更快地走错路。稳妥的表达可以加一句“名称可能已调整，请先搜索确认”，或者同时给出页面行为和可见文案，让代理交叉验证。&lt;/p&gt;
&lt;p&gt;所以更实用的结论不是“需求里多塞关键词”，而是：&lt;strong&gt;把人的页面印象翻译成仓库可检索的坐标，再用行为和验收条件约束修改结果。&lt;/strong&gt; 前者减少寻找代码的成本，后者减少写错代码的空间。两部分同时存在，编码模型的效率提升才更稳定。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://arxiv.org/abs/1706.03762&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Attention Is All You Need&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://arxiv.org/abs/2005.14165&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Language Models are Few-Shot Learners&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://openai.com/academy/what-is-ai/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI Academy：AI fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://openai.com/index/why-language-models-hallucinate/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI：Why language models hallucinate&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;大模型编码写需求的时候，如果能带上一些关键词，比如：关联的函数名称、前端关联的样式名称，能让模型的效率更高，稍微讲解下 LLM 的原来，当前大模型的原理，解释下我这样做为什么更好&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这篇保留了“函数名、样式名能提高编码效率”的现场判断，并补上语言模型生成与编码代理检索之间的区别。压掉了完整 Transformer 教程、模型代际比较和泛化的提示词清单，避免机制介绍抢走工程问题的主线。&lt;/p&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Hermes 搜索不稳：SearXNG 出站交给 Mihomo</title>
        <link>https://ttf248.life/p/hermes-searxng-mihomo-proxy/</link>
        <pubDate>Sun, 21 Jun 2026 20:33:01 +0800</pubDate>
        
        <guid>https://ttf248.life/p/hermes-searxng-mihomo-proxy/</guid>
        <description>&lt;p&gt;Hermes 接上 SearXNG 以后，表面上已经把搜索问题收回到了本机：&lt;code&gt;SEARXNG_URL=http://localhost:8888&lt;/code&gt;。但境内服务器真正容易卡住的地方不在 Hermes 到 SearXNG 这一跳，而在 SearXNG 自己还要访问 Google、DuckDuckGo、Brave、Startpage 这些搜索源。&lt;/p&gt;
&lt;p&gt;我最后把链路拆成三层：Hermes 只访问本机 SearXNG；SearXNG 在配置里显式把 HTTP/HTTPS 出站请求交给 Mihomo；Mihomo 单独负责订阅、健康检查和自动选节点。这样做的好处不是“配置更酷”，而是出问题时可以分层看：Hermes 有没有请求本地搜索，SearXNG 有没有返回 JSON，Mihomo 有没有可用节点，订阅本身有没有解析失败。&lt;/p&gt;
&lt;p&gt;整体链路是这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Hermes Agent
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -&amp;gt; http://localhost:8888
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -&amp;gt; SearXNG
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -&amp;gt; http://mihomo:7890
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -&amp;gt; Mihomo 自动选择可用节点
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -&amp;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=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1:8888  -&amp;gt; SearXNG
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1:7897  -&amp;gt; Mihomo mixed proxy，用于调试
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1:9097  -&amp;gt; Mihomo controller，用于本机管理
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;SearXNG 和 Mihomo 在同一个 Docker 网络里，所以 SearXNG 不访问宿主机的 &lt;code&gt;127.0.0.1:7897&lt;/code&gt;，而是访问容器名：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;http://mihomo:7890
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;searxng-里最容易漏的两处&#34;&gt;SearXNG 里最容易漏的两处
&lt;/h2&gt;&lt;p&gt;Hermes 调 SearXNG 时会请求 JSON。SearXNG 官方 Search API 文档也写得很清楚：&lt;code&gt;format&lt;/code&gt; 参数能不能用，取决于 &lt;code&gt;settings.yml&lt;/code&gt; 里 &lt;code&gt;search.formats&lt;/code&gt; 是否启用；请求未启用的格式会返回 403。所以这里不能只保留默认 HTML：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;safe_search&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;autocomplete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;formats&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;第二处是代理。不要只指望 Docker 环境变量或系统级 &lt;code&gt;http_proxy&lt;/code&gt; 被应用层稳定继承。SearXNG 的请求是它自己发起的，最直接的办法是在 &lt;code&gt;outgoing.proxies&lt;/code&gt; 里写清楚：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;outgoing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;request_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;15.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;max_request_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;40.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;extra_proxy_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;proxies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;http://&amp;#34;: &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;http://mihomo:7890&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;https://&amp;#34;: &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;http://mihomo:7890&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;SearXNG 默认配置示例里能看到 &lt;code&gt;all://:&lt;/code&gt; 这种写法，所以它不是一个天然错误的配置项。但在我这次使用的环境里，&lt;code&gt;all://:&lt;/code&gt; 没有按预期匹配，最后稳定下来的是把 &lt;code&gt;http://&lt;/code&gt; 和 &lt;code&gt;https://&lt;/code&gt; 分开写。文章里保留这个边界，是为了避免把一次实测写成所有版本的定论。&lt;/p&gt;
&lt;p&gt;完整的 SearXNG 关键配置可以压到下面这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;use_default_settings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;general&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;instance_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Hermes SearXNG&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;search&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;safe_search&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;autocomplete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;formats&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;secret_key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;请替换成随机密钥&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;limiter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image_proxy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;bind_address&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;valkey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;valkey://valkey:6379/0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;outgoing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;request_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;15.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;max_request_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;40.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;extra_proxy_timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;proxies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;http://&amp;#34;: &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;http://mihomo:7890&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;https://&amp;#34;: &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;http://mihomo:7890&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;engines&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;bing&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortcut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;bi&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;bing news&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortcut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;bin&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;google&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortcut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;go&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;brave&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortcut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;br&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;duckduckgo&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortcut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ddg&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;startpage&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortcut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;sp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8.0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;wikipedia&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortcut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;wp&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;arxiv&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shortcut&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;arx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;sogou&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;360search&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;disabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;ui&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;static_use_hash&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里有一个小坑：SearXNG 默认引擎配置里，Bing 新闻的 &lt;code&gt;name&lt;/code&gt; 是 &lt;code&gt;bing news&lt;/code&gt;，&lt;code&gt;engine&lt;/code&gt; 才是 &lt;code&gt;bing_news&lt;/code&gt;。在 &lt;code&gt;use_default_settings: true&lt;/code&gt; 下，&lt;code&gt;engines&lt;/code&gt; 是按 &lt;code&gt;name&lt;/code&gt; 合并覆盖的，所以这里要写 &lt;code&gt;name: bing news&lt;/code&gt;，不要写成 &lt;code&gt;name: bing_news&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;mihomo-只做一件事给-searxng-一个稳定出口&#34;&gt;Mihomo 只做一件事：给 SearXNG 一个稳定出口
&lt;/h2&gt;&lt;p&gt;Mihomo 这边不需要接管整台服务器，也不需要让 Docker 全局走代理。它只开一个 mixed port，让同一个 Docker 网络里的 SearXNG 能访问：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;mixed-port&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7890&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;allow-lan&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;bind-address&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;mode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;rule&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;log-level&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;ipv6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;external-controller&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;9090&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;secret&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;请替换成随机密钥&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;profile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;store-selected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;store-fake-ip&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;proxy-providers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;sub&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;你的订阅地址&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3600&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;./proxy_providers/sub.yaml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;health-check&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;enable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;https://www.gstatic.com/generate_204&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;lazy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;expected-status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;204&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;proxy-groups&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;AUTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;url-test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;sub&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;https://www.gstatic.com/generate_204&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;interval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5000&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tolerance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;lazy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;expected-status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;m&#34;&gt;204&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;PROXY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;proxies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;AUTO&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;DIRECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;sub&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;rules&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;MATCH,PROXY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这份配置的意思很窄：订阅每 3600 秒更新一次，节点每 300 秒做一次健康检查，&lt;code&gt;AUTO&lt;/code&gt; 用 &lt;code&gt;url-test&lt;/code&gt; 按延迟选择节点，所有流量最后匹配到 &lt;code&gt;PROXY&lt;/code&gt;。新配置第一次启动时，&lt;code&gt;PROXY&lt;/code&gt; 列表里第一个就是 &lt;code&gt;AUTO&lt;/code&gt;；如果以后你在控制面板里手动切过节点，&lt;code&gt;store-selected: true&lt;/code&gt; 会保留选择，这时“默认走 AUTO”就不一定成立了。&lt;/p&gt;
&lt;p&gt;还要注意订阅格式。&lt;code&gt;proxy-providers&lt;/code&gt; 适合 provider 格式或能被 Mihomo 作为 provider 解析的订阅；有些服务商给的是完整 Clash 配置，有些给的是节点列表。如果日志里报解析失败，不要先怀疑 SearXNG，先去服务商后台切成 &lt;code&gt;Clash Meta&lt;/code&gt;、&lt;code&gt;Mihomo&lt;/code&gt; 或 &lt;code&gt;Proxy Provider&lt;/code&gt; 格式。&lt;/p&gt;
&lt;h2 id=&#34;改造脚本&#34;&gt;改造脚本
&lt;/h2&gt;&lt;p&gt;下面这个脚本适合已经把 SearXNG 放在 &lt;code&gt;/opt/searxng&lt;/code&gt; 的机器。它会备份现有 &lt;code&gt;docker-compose.yml&lt;/code&gt; 和 &lt;code&gt;searxng/settings.yml&lt;/code&gt;，复用本机已有的 &lt;code&gt;metacubex/mihomo&lt;/code&gt; 镜像，并把 SearXNG、Valkey、Mihomo 放进同一个 compose 项目。&lt;/p&gt;
&lt;p&gt;脚本不会主动拉取 Mihomo 镜像；SearXNG 和 Valkey 如果本机没有对应镜像，&lt;code&gt;docker compose up&lt;/code&gt; 是否能启动取决于你的本机镜像和网络环境。完全离线环境里，先把三个镜像都准备好。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo bash -s &lt;span class=&#34;s&#34;&gt;&amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;set -euo pipefail
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;APP_DIR=&amp;#34;/opt/searxng&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;COMPOSE_FILE=&amp;#34;$APP_DIR/docker-compose.yml&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;SETTINGS_FILE=&amp;#34;$APP_DIR/searxng/settings.yml&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;MIHOMO_DIR=&amp;#34;$APP_DIR/mihomo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;if [ ! -f &amp;#34;$COMPOSE_FILE&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  echo &amp;#34;未找到 $COMPOSE_FILE，请确认 SearXNG 是否部署在 /opt/searxng&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;if [ ! -f &amp;#34;$SETTINGS_FILE&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  echo &amp;#34;未找到 $SETTINGS_FILE，请确认 SearXNG settings.yml 路径&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;if docker image inspect metacubex/mihomo:latest &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  MIHOMO_IMAGE=&amp;#34;metacubex/mihomo:latest&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  MIHOMO_IMAGE=&amp;#34;$(docker images --format &amp;#39;{{.Repository}}:{{.Tag}}&amp;#39; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    | awk -F: &amp;#39;$1==&amp;#34;metacubex/mihomo&amp;#34; &amp;amp;&amp;amp; $2!=&amp;#34;&amp;lt;none&amp;gt;&amp;#34; {print; exit}&amp;#39;)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;if [ -z &amp;#34;${MIHOMO_IMAGE:-}&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  echo &amp;#34;未检测到可用的本地 metacubex/mihomo 镜像。&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  echo &amp;#34;请先确认：docker images | grep -i mihomo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;检测到本地 Mihomo 镜像：$MIHOMO_IMAGE&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;LOCAL_MIHOMO_IMAGE=&amp;#34;searxng-mihomo-local:latest&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;docker tag &amp;#34;$MIHOMO_IMAGE&amp;#34; &amp;#34;$LOCAL_MIHOMO_IMAGE&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;printf &amp;#34;请输入你的 Clash/Mihomo 订阅 URL: &amp;#34; &amp;gt; /dev/tty
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;IFS= read -r -s SUB_URL &amp;lt; /dev/tty
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;printf &amp;#34;\n&amp;#34; &amp;gt; /dev/tty
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;if [ -z &amp;#34;$SUB_URL&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  echo &amp;#34;订阅 URL 为空，退出。&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;mkdir -p &amp;#34;$MIHOMO_DIR/proxy_providers&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;chmod 700 &amp;#34;$MIHOMO_DIR&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;TS=&amp;#34;$(date +%Y%m%d-%H%M%S)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;cp -a &amp;#34;$COMPOSE_FILE&amp;#34; &amp;#34;$COMPOSE_FILE.bak.$TS&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;cp -a &amp;#34;$SETTINGS_FILE&amp;#34; &amp;#34;$SETTINGS_FILE.bak.$TS&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;已备份：&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;  $COMPOSE_FILE.bak.$TS&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;  $SETTINGS_FILE.bak.$TS&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;MIHOMO_SECRET=&amp;#34;$(openssl rand -hex 16)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;cat &amp;gt; &amp;#34;$MIHOMO_DIR/config.yaml&amp;#34; &amp;lt;&amp;lt;YAML
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;mixed-port: 7890
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;allow-lan: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;bind-address: &amp;#39;*&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;mode: rule
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;log-level: info
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;ipv6: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;external-controller: 0.0.0.0:9090
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;secret: &amp;#34;$MIHOMO_SECRET&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;profile:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  store-selected: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  store-fake-ip: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;proxy-providers:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  sub:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    type: http
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    url: &amp;#34;$SUB_URL&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    interval: 3600
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    path: ./proxy_providers/sub.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    health-check:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      enable: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      url: https://www.gstatic.com/generate_204
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      interval: 300
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      timeout: 5000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      lazy: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      expected-status: 204
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;proxy-groups:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: AUTO
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    type: url-test
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    use:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - sub
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    url: https://www.gstatic.com/generate_204
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    interval: 300
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    timeout: 5000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    tolerance: 50
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    lazy: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    expected-status: 204
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: PROXY
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    type: select
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    proxies:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - AUTO
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - DIRECT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    use:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - sub
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;rules:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - MATCH,PROXY
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;YAML
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;chmod 600 &amp;#34;$MIHOMO_DIR/config.yaml&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;OLD_SECRET=&amp;#34;$(grep -E &amp;#39;^[[:space:]]*secret_key:&amp;#39; &amp;#34;$SETTINGS_FILE&amp;#34; | head -n1 | sed -E &amp;#34;s/.*secret_key:[[:space:]]*[&amp;#39;\&amp;#34;]?([^&amp;#39;\&amp;#34;]+)[&amp;#39;\&amp;#34;]?.*/\1/&amp;#34; || true)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;if [ -z &amp;#34;$OLD_SECRET&amp;#34; ] || [ &amp;#34;$OLD_SECRET&amp;#34; = &amp;#34;secret_key:&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  OLD_SECRET=&amp;#34;$(openssl rand -hex 32)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;cat &amp;gt; &amp;#34;$SETTINGS_FILE&amp;#34; &amp;lt;&amp;lt;YAML
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;use_default_settings: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;general:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  instance_name: &amp;#34;Hermes SearXNG&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;search:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  safe_search: 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  autocomplete: &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  formats:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    - html
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    - json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;server:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  secret_key: &amp;#34;$OLD_SECRET&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  limiter: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  image_proxy: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  bind_address: &amp;#34;0.0.0.0&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;valkey:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  url: valkey://valkey:6379/0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;outgoing:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  request_timeout: 15.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  max_request_timeout: 40.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  extra_proxy_timeout: 10
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  proxies:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    &amp;#34;http://&amp;#34;: &amp;#34;http://mihomo:7890&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    &amp;#34;https://&amp;#34;: &amp;#34;http://mihomo:7890&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;engines:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: bing
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    shortcut: bi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: bing news
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    shortcut: bin
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: google
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    shortcut: go
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    timeout: 8.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: brave
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    shortcut: br
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    timeout: 8.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: duckduckgo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    shortcut: ddg
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    timeout: 8.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: startpage
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    shortcut: sp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    timeout: 8.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: wikipedia
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    shortcut: wp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: arxiv
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    shortcut: arx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: sogou
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  - name: 360search
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    disabled: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;ui:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  static_use_hash: true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;YAML
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;cat &amp;gt; &amp;#34;$COMPOSE_FILE&amp;#34; &amp;lt;&amp;lt;YAML
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;services:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  searxng:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    image: docker.io/searxng/searxng:latest
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    container_name: searxng
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    restart: unless-stopped
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - &amp;#34;127.0.0.1:8888:8080&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - ./searxng/settings.yml:/etc/searxng/settings.yml:ro
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - searxng-cache:/var/cache/searxng:rw
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    depends_on:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - valkey
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - mihomo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - searxng-net
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    logging:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      driver: json-file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      options:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        max-size: &amp;#34;2m&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        max-file: &amp;#34;3&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  valkey:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    image: docker.io/valkey/valkey:8-alpine
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    container_name: searxng-valkey
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    restart: unless-stopped
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    command: valkey-server --save 30 1 --loglevel warning
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - valkey-data:/data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - searxng-net
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    logging:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      driver: json-file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      options:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        max-size: &amp;#34;2m&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        max-file: &amp;#34;3&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  mihomo:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    image: $LOCAL_MIHOMO_IMAGE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    container_name: searxng-mihomo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    restart: unless-stopped
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    command: [&amp;#34;-d&amp;#34;, &amp;#34;/root/.config/mihomo&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - &amp;#34;127.0.0.1:7897:7890&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - &amp;#34;127.0.0.1:9097:9090&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - ./mihomo:/root/.config/mihomo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      - searxng-net
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;    logging:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      driver: json-file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;      options:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        max-size: &amp;#34;2m&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;        max-file: &amp;#34;3&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;networks:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  searxng-net:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;volumes:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  searxng-cache:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  valkey-data:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;YAML
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;cd &amp;#34;$APP_DIR&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;if docker compose version &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  DC=&amp;#34;docker compose&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;elif command -v docker-compose &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  DC=&amp;#34;docker-compose&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;else
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  echo &amp;#34;未检测到 docker compose / docker-compose&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;启动服务...&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;$DC up -d --no-build
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;等待服务启动...&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;sleep 12
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;容器状态：&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;$DC ps
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;Mihomo 最近日志：&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;$DC logs --tail=80 mihomo || true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;测试 Mihomo 本机代理端口 127.0.0.1:7897：&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;curl -I -sS --max-time 25 --proxy http://127.0.0.1:7897 https://www.gstatic.com/generate_204 || true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;测试 SearXNG JSON 搜索：&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;curl -sS --max-time 50 &amp;#34;http://127.0.0.1:8888/search?q=openai%20gpt&amp;amp;format=json&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;  | python3 -c &amp;#39;import sys,json; d=json.load(sys.stdin); print(&amp;#34;OK:&amp;#34;, len(d.get(&amp;#34;results&amp;#34;, [])), &amp;#34;results&amp;#34;)&amp;#39; || true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;完成。&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;Hermes 继续使用：SEARXNG_URL=http://localhost:8888&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;SearXNG 出站代理：searxng -&amp;gt; http://mihomo:7890&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;Mihomo 本机调试代理：http://127.0.0.1:7897&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;Mihomo 控制端口：http://127.0.0.1:9097&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;echo &amp;#34;Mihomo 配置文件：$MIHOMO_DIR/config.yaml&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;hermes-只保留搜索入口&#34;&gt;Hermes 只保留搜索入口
&lt;/h2&gt;&lt;p&gt;Hermes 侧不用知道 Mihomo。它只需要知道本机有一个 SearXNG：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;nano ~/.hermes/.env
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;写入：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;SEARXNG_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;http://localhost:8888
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在 &lt;code&gt;~/.hermes/config.yaml&lt;/code&gt; 里指定搜索后端：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;web&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;search_backend&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;searxng&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你装的是 Hermes 官方 SearXNG skill，可以继续使用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;hermes skills install official/research/searxng-search
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果 Hermes 是 user service：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl --user restart hermes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl --user status hermes --no-pager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里还有一个边界：SearXNG 负责搜索，不负责网页正文抽取。Hermes 文档里也把 search backend 和 extract backend 分开。如果后续要让 Hermes 读取搜索结果页面正文，还需要再给 &lt;code&gt;web.extract_backend&lt;/code&gt; 配 Firecrawl、Tavily、Exa、Parallel 或其他抽取方案。&lt;/p&gt;
&lt;h2 id=&#34;验证不要跳层&#34;&gt;验证不要跳层
&lt;/h2&gt;&lt;p&gt;先测 Mihomo，不要一上来就问 Hermes：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -I --proxy http://127.0.0.1:7897 https://www.gstatic.com/generate_204
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;能返回 HTTP 状态，说明宿主机调试代理端口可用。然后测 SearXNG JSON：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -s --max-time &lt;span class=&#34;m&#34;&gt;50&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://127.0.0.1:8888/search?q=openai%20gpt&amp;amp;format=json&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; python3 -c &lt;span class=&#34;s1&#34;&gt;&amp;#39;import sys,json; d=json.load(sys.stdin); print(len(d.get(&amp;#34;results&amp;#34;, [])), &amp;#34;results&amp;#34;)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果这里出现 403，优先看 &lt;code&gt;search.formats&lt;/code&gt; 有没有 &lt;code&gt;json&lt;/code&gt;。如果这里超时或结果为空，再看 SearXNG 日志：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /opt/searxng
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose logs -f searxng
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在 Hermes 里发一条会触发搜索的请求：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;请联网搜索 OpenAI GPT 的最新资料，列出来源链接。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;日志里如果出现 &lt;code&gt;/search?...format=json&lt;/code&gt;，说明 Hermes 已经在调本地 SearXNG。再看 Mihomo：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /opt/searxng
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose logs -f mihomo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重点看订阅更新、provider、health check、AUTO 这些信息。如果 Mihomo 连订阅都没解析出来，SearXNG 配得再对也没用。&lt;/p&gt;
&lt;h2 id=&#34;几个不要省掉的边界&#34;&gt;几个不要省掉的边界
&lt;/h2&gt;&lt;p&gt;第一，不要把端口暴露到公网。本文里的 compose 只绑定本机：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;127.0.0.1:8888:8080&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;127.0.0.1:7897:7890&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;127.0.0.1:9097:9090&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&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=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8888&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;8080&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7897&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7890&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;m&#34;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;9097&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;9090&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;否则搜索服务、代理端口和 Mihomo 控制端口都有被公网滥用的风险。&lt;code&gt;external-controller: 0.0.0.0:9090&lt;/code&gt; 是容器内监听，真正决定外部能不能访问的是 compose 的端口绑定。&lt;/p&gt;
&lt;p&gt;第二，如果 Google 或 Startpage 总是超时，不要急着把整套链路推翻。先保留 Bing、Brave、DuckDuckGo、Wikipedia、arXiv，等 Mihomo 订阅和健康检查稳定以后，再打开更容易触发验证码或超时的引擎。&lt;/p&gt;
&lt;p&gt;第三，脚本的回滚点很直接：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /opt/searxng
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp docker-compose.yml.bak.你的时间戳 docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp searxng/settings.yml.bak.你的时间戳 searxng/settings.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我更倾向于这种拆层方案，而不是给整台服务器套全局代理。全局代理的问题是影响面太大，出了问题以后很难判断是系统环境、Docker、应用配置还是代理节点本身。Hermes、SearXNG、Mihomo 各自只做一件事，排障的时候反而省事。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/web-search&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent：Web Search &amp;amp; Extract&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/skills/optional/research/research-searxng-search&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent：Free meta-search via SearXNG&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.searxng.org/dev/search_api.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SearXNG Search API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.searxng.org/admin/settings/settings.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SearXNG settings.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.searxng.org/admin/settings/settings_outgoing.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SearXNG outgoing settings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.searxng.org/admin/settings/settings_engines.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SearXNG engines settings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/searxng/searxng/blob/master/searx/settings.yml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SearXNG 默认 settings.yml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://wiki.metacubex.one/en/config/proxy-providers/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Mihomo proxy-providers configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://wiki.metacubex.one/en/config/proxy-groups/url-test/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Mihomo url-test proxy group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;p&gt;这篇保留了原始材料里最有操作价值的部分：分层架构、SearXNG JSON、显式 outgoing proxy、Mihomo provider 和健康检查、Docker Compose、Hermes 配置、验证命令、常见问题和回滚。压掉的是重复解释，以及容易误导的绝对化表述；例如 &lt;code&gt;all://:&lt;/code&gt; 不是通用无效配置，只是在这次环境里没有按预期匹配。&lt;/p&gt;
&lt;p&gt;另外修正了一处配置名：SearXNG 的 Bing 新闻引擎应写 &lt;code&gt;name: bing news&lt;/code&gt;。如果写成 &lt;code&gt;name: bing_news&lt;/code&gt;，它和默认引擎名对不上，风险比普通拼写问题更高。&lt;/p&gt;
&lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;用户提供了一份题为《中国境内服务器部署 Hermes + SearXNG + Mihomo：让 Hermes 搜索走代理并自动选择可用节点》的完整部署材料，要求整理分析、确认文章内容没有错误，然后调用博客写作 skill 写成文章。&lt;/p&gt;
&lt;p&gt;材料包含：背景、目标架构、为什么不用系统代理、SearXNG settings.yml、Mihomo config.yaml、Docker Compose、改造脚本、Hermes 配置、验证链路、常见问题、回滚方法和最终效果。&lt;/p&gt;
&lt;/blockquote&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Loop engineering 把人挪到检查点</title>
        <link>https://ttf248.life/p/loop-engineering-human-checkpoints/</link>
        <pubDate>Sun, 14 Jun 2026 07:35:00 +0800</pubDate>
        
        <guid>https://ttf248.life/p/loop-engineering-human-checkpoints/</guid>
        <description>&lt;p&gt;昨天参加 MiniMax 线下开发者大会，回来以后有个词一直没绕过去：loop engineering。&lt;/p&gt;
&lt;p&gt;我一开始以为它只是把 agent 多跑几轮。这个理解太浅了。真正让我卡住的地方是，AI 写代码的速度已经比人审核代码的速度快太多，如果人的参与还停在“每一步都看、每一步都拍板”，那生产力最后会被人的检查速度锁住。&lt;/p&gt;
&lt;p&gt;这和我最近用 Codex 的感受能接上。之前写 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/codex-goal-command-explained/&#34; &gt;那篇 goal 文章&lt;/a&gt; 时，我更多关注的是“完成条件”这件事：目标是什么，边界在哪里，用什么验证，什么时候算完成。&lt;/p&gt;
&lt;p&gt;大会以后再回头看，goal 只是 loop engineering 里很小但很关键的一块。它解决的是单次任务怎么不靠人反复提醒也能往前走。更大的问题是：哪些任务值得交给循环，哪些检查点必须留给人，哪些判断可以提前写进系统，哪些责任不能推给 AI。&lt;/p&gt;
&lt;p&gt;我现在觉得最契合的场景有两个。&lt;/p&gt;
&lt;p&gt;一个是性能优化。它天然有指标，最好还能自动复测。你告诉 agent：接口 P95 要降到多少，首屏要控制在多少毫秒，包体要降到多少 KB，回归测试不能挂。它每改一轮就跑一次 benchmark、profile 或压测，没达标就继续找瓶颈。这里人的价值不是每次看它改了哪一行，而是先给出可验收的指标，再在关键节点判断“这个优化有没有改变业务语义”。&lt;/p&gt;
&lt;p&gt;另一个是 UI 重构。前提是要有明确设计稿，最好能做到 1:1 还原。没有设计稿时，人会不断被拉回“这个边距不对”“这个颜色不舒服”“这个交互不像”的琐碎判断里；有了设计稿，AI 的循环就可以围着截图、DOM、样式和视觉差异来收敛。人不必跟着每一次 CSS 调整跑，但要对最终界面负责。&lt;/p&gt;
&lt;p&gt;这两个场景的共同点是，验收物不是一句“做得好一点”。性能优化有数字，UI 重构有设计稿。循环能跑起来，不是因为 AI 更听话，而是因为任务本身有可比较的目标。&lt;/p&gt;
&lt;h2 id=&#34;multica-像一块协作板&#34;&gt;Multica 像一块协作板
&lt;/h2&gt;&lt;p&gt;大会现场还碰到开源圈的人介绍项目，其中 Multica 挺有意思。我回来查了一下，它不是一个新的单体 coding agent，而是把 Claude Code、Codex、GitHub Copilot CLI、OpenCode、Gemini、Kimi、Cursor Agent 等工具接进同一个任务协作层。&lt;/p&gt;
&lt;p&gt;它的 README 里把 agent 写成 teammate：可以被分配 issue，会汇报 blocker，会更新状态，也会出现在看板、评论和任务生命周期里。文档里更具体一点：agent 是 workspace 的一等成员，可以被 assign issue、在评论里发言、被 &lt;code&gt;@&lt;/code&gt; 提及；创建 agent 时可以选择背后的 AI coding tool，并配置 instructions、model、环境变量、CLI 参数、可见性和并发限制。&lt;/p&gt;
&lt;p&gt;这就把“用哪个模型”从一次 prompt 里的临时选择，变成了团队协作里的角色配置。&lt;/p&gt;
&lt;p&gt;它最有意思的地方，不是“多代理”这个词，而是可以给不同 agent 配不同身份。比如一个 agent 用便宜模型做重复修复，一个 agent 用更强模型做架构判断，一个 agent 只做 review，不改代码；再配合 Squad 这种机制，由 leader agent 根据 issue 内容把任务路由给合适成员。&lt;/p&gt;
&lt;p&gt;我没有完整跑一遍 Multica，所以这里只能把它当作一个公开资料可核对的样本看。但这个设计方向和 loop engineering 是同一类问题：不是让一个 AI 把所有事干完，而是把任务、角色、模型、审核和状态流转放进同一个循环里。&lt;/p&gt;
&lt;p&gt;如果公司真要落地 AI 写代码，这个差别很重要。&lt;/p&gt;
&lt;p&gt;现在模型价格不一样，能力边界也不一样。所有任务都用最贵的闭源模型，成本不一定扛得住；所有任务都用便宜模型，返工和漏审又可能把成本搬到后面。更现实的做法是分层：低风险、重复、边界清楚的活交给便宜模型先跑；关键变更、架构取舍和最终审核，留给更强的模型或人。&lt;/p&gt;
&lt;p&gt;闭源强模型用来做审核，我觉得是一个不错的位置。审核不只是“挑语法错误”，它要看需求有没有被改坏，边界有没有被越过，测试是不是只覆盖了表面，提交说明是不是掩盖了风险。这类任务对判断力要求更高，但调用频次未必像实现阶段那么大，成本账更容易接受。&lt;/p&gt;
&lt;h2 id=&#34;人少参与不等于人不负责&#34;&gt;人少参与，不等于人不负责
&lt;/h2&gt;&lt;p&gt;圆桌讨论里有个观点我很认同：代码是 AI 写的，但代码是人类提交的。&lt;/p&gt;
&lt;p&gt;这句话听起来像责任提醒，其实也是流程设计原则。AI 可以写代码、改代码、跑测试、生成 PR 说明，甚至可以让另一个模型先审一轮。但最后把代码合进主干的人，不能说“这是 AI 写的，和我没关系”。&lt;/p&gt;
&lt;p&gt;公司里要的不是谁在键盘上敲字符，而是谁对结果负责。你提交了，就表示你愿意为这次变更的业务语义、风险边界、测试证据和回滚预案负责。AI 参与得越深，这件事越不能含糊。&lt;/p&gt;
&lt;p&gt;所以 loop engineering 不是把人彻底从流程里删掉。它更像是重新安排人的位置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;任务开始前，人写清目标、边界和验收条件。&lt;/li&gt;
&lt;li&gt;循环运行时，系统和 agent 自己处理重复尝试、测试、修复和状态同步。&lt;/li&gt;
&lt;li&gt;关键节点上，人看证据、看差异、看风险，而不是逐行追着 AI 的产出跑。&lt;/li&gt;
&lt;li&gt;提交代码时，人承担结果责任，不能把 AI 当免责理由。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这也是我为什么会把 goal、性能指标、设计稿、Multica 这几个看似分散的东西连在一起。它们都在把人的判断提前、外显、结构化。人参与得少一点，但参与的位置更关键。&lt;/p&gt;
&lt;p&gt;这件事如果做不好，就会变成另一种低效：AI 产出很多，人审不过来，最后团队靠“感觉上差不多”合代码；出了问题以后，又把锅甩给 AI。那不是工程化，只是把混乱换了个生产者。&lt;/p&gt;
&lt;p&gt;真正值得追的循环，不是让 AI 一直写，而是让每一轮写作、修改、测试和审核都能回到同一张验收单上。人的工作从“盯着它写”变成“定义什么算好、检查证据够不够、为提交负责”。这可能才是 loop engineering 这个词让我一直想的地方。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/multica-ai/multica&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Multica GitHub README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://multica.ai/docs/agents&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Multica Docs: Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://multica.ai/docs/agents-create&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Multica Docs: Create and configure an agent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://multica.ai/docs/providers&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Multica Docs: AI coding tools matrix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://multica.ai/docs/squads&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Multica Docs: Squads&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 昨天参加 Minimax 线下开发者大会，有个东西一直没绕过去，loop engineering，近期在用 codex 代码开发，也喜欢用 goal，也写过关于 goal 的文章，发现两个场景，最契合，性能优化，直接给出我要求的性能指标、UI 重构，提供设计稿，要求 1:1 还原。回到开始说的 loop engineering，AI 时代的编程，由于 AI 的生产力远超人类，人类审核的速度，完全无法追平，AI 产出的速度，但是现在的 vibe coding，很多决策还是需要人类参与的，人的参与变多了以后，整个流程就不容易完全自动化。loop engineering 的理念，更多就是降低人类的参与程度。还碰到了开源圈的人来宣讲他们的项目，有一个项目比较有意思，Multica，你查下相关资料，简单的介绍下，除开他的设计理念，最有意思的地方在，不同给的模型，给不同的身份，能有效的利用各家的大模型，毕竟现在模型价格不一样，闭源模型来做审核是个不错的思路。关于 AI 代码在公司落地，圆桌讨论的时候，有个观点也是一致的，代码是 AI 写的，但是代码是人类提交的，你需要对你提交的代码负责，这表现的是你的责任心、你做事情的态度。你不能说这是AI写的，和你没关系。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;保留 2026-06-13 参加 MiniMax 线下开发者大会这个现场触发点，没有把它扩写成大会报道。&lt;/li&gt;
&lt;li&gt;把主线收束在“人的参与位置怎么变”，而不是逐条解释 loop engineering、goal、Multica 和公司责任。&lt;/li&gt;
&lt;li&gt;Multica 部分只写公开资料能支撑的能力，并把“闭源模型做审核”明确放在作者判断里。&lt;/li&gt;
&lt;li&gt;压掉了对具体模型价格、公司管理制度和代码审查流程的展开，避免文章从工作流观察跑成管理制度建议。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Codex 写界面扑街之后，让 ChatGPT 画 UI</title>
        <link>https://ttf248.life/p/codex-chatgpt-ui-mockup-workflow/</link>
        <pubDate>Sun, 14 Jun 2026 05:14:22 +0800</pubDate>
        
        <guid>https://ttf248.life/p/codex-chatgpt-ui-mockup-workflow/</guid>
        <description>&lt;p&gt;最开始我以为只是前端还没来得及收拾。&lt;code&gt;strategy_studio&lt;/code&gt; 的业务逻辑一直在往前走：行情同步、PostgreSQL、异步回测、报告落库和研究工作台都陆续接上了，但页面看起来越来越像“功能先上车，界面后补票”。等我想让 Codex 直接破坏性重构 UI 时，问题才真正露出来：提示词越写越狠，界面还是像在旧格子里重新挪组件。&lt;/p&gt;
&lt;p&gt;这不是第一次用 Codex 改代码。后端链路、API 对接、测试修复、报告生成这类任务，我通常直接把目标扔进去，让它读文件、改代码、跑验证、提交。问题出在前端重构这里：我没有先做 UI 稿，前面又已经把业务结构铺开了。旧界面一旦存在，模型看到的就不只是需求，还有现成组件、现成路由、现成样式和一堆历史惯性。&lt;/p&gt;
&lt;p&gt;我一开始的反应也很普通：继续加提示词。允许破坏性重构，重新设计 UI 和交互逻辑，元素不要太密集，适配 24 寸和 32 寸显示器，聚焦核心功能，不要保守。每句话单独看都对，但合在一起仍然缺一个东西：最后到底应该长什么样。&lt;/p&gt;
&lt;h2 id=&#34;卡住的不是按钮颜色&#34;&gt;卡住的不是按钮颜色
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;strategy_studio&lt;/code&gt; 不是一个只有两三个按钮的小工具。README 里对它的定位已经是中文优先的策略研究平台：Next.js 前端、FastAPI API、PostgreSQL、Worker、Scheduler 和 Yahoo 同步链路一起跑。前端 README 又把页面收敛成一个研究工作台：样本准备、实验配置、任务跟踪、结果复盘、模板维护，还有报告详情和运行维护入口。&lt;/p&gt;
&lt;p&gt;这种项目的前端问题，表面上是“不够好看”，实际是信息秩序没定下来。用户先看行情，还是先看回测任务？报告详情里曲线、指标、输入参数和重跑按钮谁更重要？首页是导航页，还是工作台？大屏上是铺满，还是留出呼吸感？&lt;/p&gt;
&lt;p&gt;这些判断只用文字描述，Codex 很容易理解成“在现有页面里优化一下”。它会改组件、调间距、换颜色，但仍然沿着旧结构走。允许破坏性重构也不够，因为破坏性重构解决的是“能不能大改代码”，不是“有没有一个可验收的视觉目标”。&lt;/p&gt;
&lt;p&gt;我白天出去一趟，晚上突然想到，别继续让 Codex 猜 UI 了。先在 ChatGPT 网页端把 UI 设计稿画出来，再把图交给 Codex 还原。&lt;/p&gt;
&lt;p&gt;当时用的提示词很直接：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;https://github.com/ttf248/strategy_studio 分析当前项目的前端代码，理解业务逻辑，作为专业的UI设计师、交互设计师，帮我重新设计一套前端页面，每个页面单独出图，记得适配 24寸、32寸显示器，元素不要太密集，方便用户使用聚焦核心功能。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这一步出来的效果，比我继续在 Codex 里堆形容词要稳定得多。它先给了一个可见的目标：页面怎么排、模块比例怎么分、视觉中心在哪里、留白大概到什么程度。Codex 后面再接手时，任务就从“凭审美重设计”变成了“按图理解业务并还原代码”。&lt;/p&gt;
&lt;h2 id=&#34;配色也要先变成材料&#34;&gt;配色也要先变成材料
&lt;/h2&gt;&lt;p&gt;第一轮图已经解决了版面问题，但默认配色还是常规后台系统的蓝白，不太符合我的审美。如果这个时候再把“不喜欢蓝白，换个高级点的配色”丢给 Codex，本质上还是让它猜。&lt;/p&gt;
&lt;p&gt;这张就是最初那版蓝白配色。它已经能说明页面结构，但气质仍然偏普通管理后台。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://ttf248.life/p/codex-chatgpt-ui-mockup-workflow/strategy-studio-white-blue-blog.webp&#34;
	width=&#34;1200&#34;
	height=&#34;675&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Strategy Studio 初版蓝白 UI 稿&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;后面我换了做法：先在 ChatGPT 网页端沟通配色方向，把底色、强调色、卡片层级和研究工具感说清楚，再让图像模型按这个方向出示意图。它不等于正式 Figma，也不是完整设计系统，但已经足够把前端重构最容易漂移的东西固定下来。&lt;/p&gt;
&lt;p&gt;最后拿到的不是一张“大而全首页”，而是按页面拆开的设计稿：首页、行情研究、回测实验、报告中心、报告详情、策略模板、运行维护。这个拆法很关键。&lt;code&gt;strategy_studio&lt;/code&gt; 的前端不是营销页，而是研究工具；首页、行情页和报告详情页承担的阅读任务不一样，不能都塞进同一套卡片网格。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://ttf248.life/p/codex-chatgpt-ui-mockup-workflow/strategy-studio-home-blog.webp&#34;
	width=&#34;1200&#34;
	height=&#34;675&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Strategy Studio 首页设计稿&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://ttf248.life/p/codex-chatgpt-ui-mockup-workflow/strategy-studio-report-detail-blog.webp&#34;
	width=&#34;1200&#34;
	height=&#34;675&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Strategy Studio 报告详情设计稿&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;图出来以后，Codex 的角色就清楚了。它不是从零当 UI 设计师，而是读现有业务逻辑、拆组件、调布局、对齐交互状态、跑 lint/build，再把旧前端重构到接近设计稿的目标状态。&lt;/p&gt;
&lt;h2 id=&#34;不是简单的网页版更聪明&#34;&gt;不是简单的“网页版更聪明”
&lt;/h2&gt;&lt;p&gt;后来我又查了一圈官方资料，想确认这件事能不能解释成“不同渠道的 ChatGPT 智力能力不一样”，或者“Codex 直接使用时功能被阉割”。这个说法太粗。&lt;/p&gt;
&lt;p&gt;OpenAI 对 Codex CLI 的描述，是本地终端里的 coding agent：它能在当前目录里读代码、改代码、运行命令。Codex web 则是在云端环境里处理代码任务。OpenAI 讲 Codex agent loop 时，重点也不是“模型凭空想出所有东西”，而是模型推理、上下文管理、工具调用、文件读写和命令执行一起构成软件任务。&lt;/p&gt;
&lt;p&gt;ChatGPT Images 是另一种入口。OpenAI Help 里对 Images in ChatGPT 的说明，是用户可以在对话里创建和编辑图片，也可以做 mockup 或创意视觉。这个产品形态天然更适合先探索视觉目标，因为它的交付物就是图，而不是马上动代码。&lt;/p&gt;
&lt;p&gt;所以这次差异，不是“谁更聪明”，而是任务形态不同。把“重新设计 UI”直接交给 Codex，它会站在代码仓库里思考：哪些文件要改、哪些组件不能破坏、怎么让构建通过。把同样的业务背景先交给 ChatGPT Images，它会先把抽象的审美和信息层级压成一张可见的目标图。&lt;/p&gt;
&lt;p&gt;这也解释了为什么后来 Codex 反而更好用。不是设计稿绕开了 Codex，而是设计稿给 Codex 补了上下文和验收标准。官方 Codex prompting 文档也强调，要给 Codex 相关文件、图片和清楚的完成标准。没有图时，“元素不要太密集”只是形容词；有图以后，它才变成可还原的布局约束。&lt;/p&gt;
&lt;h2 id=&#34;更顺的链路&#34;&gt;更顺的链路
&lt;/h2&gt;&lt;p&gt;这次之后，我会把这类前端重构拆成三段。&lt;/p&gt;
&lt;p&gt;第一段，先做视觉探索。让 ChatGPT 网页端读仓库、理解业务、按页面出图，再围绕配色、密度、大屏适配和视觉层级来回调。这个阶段的产物不是代码，而是目标图。&lt;/p&gt;
&lt;p&gt;第二段，把目标图和工程约束交给 Codex。提示词不要停在“重新设计 UI”，而要写清楚：以哪些图片为目标，哪些业务逻辑不能破坏，哪些页面允许破坏性重构，哪些测试和构建必须通过。&lt;/p&gt;
&lt;p&gt;第三段，进入工程还原。这里才适合让 Codex 开 goal 或连续任务模式：先读前端结构，再按页面拆分实施，每个页面完成后跑验证，最后统一检查交互和响应式。&lt;/p&gt;
&lt;p&gt;我以前容易把“提示词写得更狠”当成解决办法。现在看，前端重构里更有用的不是狠，而是把缺失的中间产物补上。没有 UI 稿时，破坏性重构只是拆得更大；有了 UI 稿以后，破坏性重构才有方向。&lt;/p&gt;
&lt;p&gt;这套流程也不是只适合 &lt;code&gt;strategy_studio&lt;/code&gt;。任何已经有业务逻辑、但前端长期靠边做边补的个人项目，都可以这么处理：先让图像模型把界面目标画出来，再让 Codex 把目标变成代码。模型之间不是谁替代谁，而是各自接自己更像工种的那一段。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/strategy_studio&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;ttf248/strategy_studio GitHub 仓库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/strategy_studio/blob/main/README.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Strategy Studio README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/strategy_studio/blob/main/frontend/README.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Strategy Studio Frontend README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/strategy_studio/tree/main/ui&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Strategy Studio UI 设计稿目录&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://help.openai.com/en/articles/11084440-images-in-chatgpt&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI Help：Images in ChatGPT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://help.openai.com/en/articles/9260256-chatgpt-capabilities-overview&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI Help：ChatGPT Capabilities Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.openai.com/codex/cli&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI Developers：Codex CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.openai.com/codex/cloud&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI Developers：Codex web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.openai.com/codex/prompting&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI Developers：Codex prompting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://openai.com/index/unrolling-the-codex-agent-loop/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI：Unrolling the Codex agent loop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 日常使用 codex 调用 ChatGPT 模型进行代码，开源项目 &lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/strategy_studio&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/ttf248/strategy_studio&lt;/a&gt; 开发的时候，开始没做 UI 稿，直接进行业务逻辑开发，导致前端界面没有规划，尝试直接在 codex 里面写提示词：允许破坏性重构，重新设计 UI、交互逻辑，怎么调整效果都不太行。这个时候，还没怀疑 codex 的问题，白天出去一趟，晚上灵光闪现，我是不是能在网页版，调用 image2 出一组 UI 设计稿，然后交付给 codex 开启 goal 模型进行还原开发，简单尝试下，没有任何问题，这是用到的提示词: &lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/strategy_studio&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/ttf248/strategy_studio&lt;/a&gt; 分析当前项目的前端代码，理解业务逻辑，作为专业的UI设计师、交互设计师，帮我重新设计一套前端页面，每个页面单独出图，记得适配 24寸、32寸显示器，元素不要太密集，方便用户使用聚焦核心功能。此时已经效果不错了，但是这个默认的配色，常规的蓝白色，不符合我的审美，继续调整方案，我们先在网页沟通一组合适的配色，然后 image 根据配色，出个演示图，再用配色方案，结合前面的提示词，我们就能拿到：https://github.com/ttf248/strategy_studio/tree/main/ui。我写的内容很多，你整理下，用一篇稿子写完，我有疑惑的地方，你自己联网查资料，提供的 &lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/strategy_studio/tree/main/ui&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://github.com/ttf248/strategy_studio/tree/main/ui&lt;/a&gt; 里面有很多图片，随便抽两张贴到博客里面。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;p&gt;这次重写保留了原文的判断和材料，但把开头从“先给完整答案”改成“先进入前端重构卡住的现场”。文章压掉了泛化模型评测，只写这次工作流里真正有效的转向：先把视觉目标变成图，再让 Codex 做工程还原。&lt;/p&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Codex goal 把完成标准交给任务本身</title>
        <link>https://ttf248.life/p/codex-goal-command-explained/</link>
        <pubDate>Wed, 27 May 2026 20:11:41 +0800</pubDate>
        
        <guid>https://ttf248.life/p/codex-goal-command-explained/</guid>
        <description>&lt;p&gt;&lt;code&gt;/goal&lt;/code&gt; 最容易被误解成一条“让 agent 多干一会儿”的命令。&lt;/p&gt;
&lt;p&gt;这当然是它的表象。你给 Codex 一个目标，它可以围着这个目标持续推进，不是一轮回答完就停。但真正值得注意的不是“跑得久”，而是它把“做到什么算完”从一句临时提醒，变成了任务本身的一部分。&lt;/p&gt;
&lt;p&gt;普通 prompt 说的是下一步要做什么。&lt;code&gt;goal&lt;/code&gt; 更像给 agent 挂了一张验收单：目标是什么，边界在哪里，哪些验证必须通过，满足什么条件才可以收工。&lt;/p&gt;
&lt;h2 id=&#34;goal-不是继续按钮&#34;&gt;goal 不是继续按钮
&lt;/h2&gt;&lt;p&gt;如果只看命令形态，&lt;code&gt;/goal&lt;/code&gt; 很像一句增强版的“继续做，直到完成”。但这会把重点带偏。&lt;/p&gt;
&lt;p&gt;长任务最麻烦的地方，不是模型愿不愿意继续，而是每一轮结束以后，谁来判断还要不要继续。&lt;/p&gt;
&lt;p&gt;你让 agent 迁移一个前端项目，它可能改了路由就觉得完成了；你让它修一个测试，它可能让当前失败用例通过就停了；你让它重写一批文章，它可能改了几篇高风险稿就认为已经处理了重点。&lt;/p&gt;
&lt;p&gt;这些停点不一定是错的，但它们经常和人的验收口径不一样。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;goal&lt;/code&gt; 要解决的就是这件事：把验收口径提前写清楚，让后续每一轮都能围着它判断。&lt;/p&gt;
&lt;p&gt;一个松散目标是这样的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/goal 帮我把前端迁到 Next.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它的问题不是短，而是没有停机条件。Codex 可以迁几页，可以顺手重构组件，也可以一直补它认为该补的东西。&lt;/p&gt;
&lt;p&gt;更可用的写法应该接近这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/goal 把订单后台从 React Router 迁到 Next.js App Router。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;登录页、订单列表、订单详情和下单页的视觉行为要和旧版一致。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;不要改 API 合约和数据库 schema。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;每完成一批页面都跑 npm run build、npm test 和 Playwright 关键路径。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;只有这些验证都通过，才算完成。
&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;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;目标&lt;/td&gt;
					&lt;td&gt;最终要出现什么结果&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;边界&lt;/td&gt;
					&lt;td&gt;哪些接口、数据、文件或行为不能顺手动&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;验证&lt;/td&gt;
					&lt;td&gt;用什么证据证明它真的完成&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;停机&lt;/td&gt;
					&lt;td&gt;满足哪些条件以后才可以停&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;goal&lt;/code&gt; 抬高的就是这四件事。&lt;/p&gt;
&lt;h2 id=&#34;为什么它能跑很久&#34;&gt;为什么它能跑很久
&lt;/h2&gt;&lt;p&gt;Codex 能在 goal 下持续推进，不是因为一次回答被拉长了。&lt;/p&gt;
&lt;p&gt;真正的工作方式更接近一个循环：计划、执行、观察工具结果、修正，再决定是否继续。构建失败、测试失败、截图不一致、lint 报错、评估样本没过，都会把任务拉回下一轮。&lt;/p&gt;
&lt;p&gt;如果目标里写了验证方式，agent 就不只是凭感觉说“应该好了”。它需要拿到证据。证据没有回来，就继续查；证据失败，就继续修；证据都通过，才有资格收工。&lt;/p&gt;
&lt;p&gt;这就是为什么 &lt;code&gt;goal&lt;/code&gt; 适合迁移、重构、批量改稿、prompt eval、长链路排障这类任务。它们共同的特点是：一轮做不完，而且完成不能只靠主观判断。&lt;/p&gt;
&lt;p&gt;反过来，这样的目标就很危险：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;/goal 想一个更高级的产品方案
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它没有边界、没有验证，也没有停机条件。agent 可能跑很久，但跑久不等于有用。至少要写清楚产出几套方案、覆盖哪些约束、用什么标准筛掉、什么时候停。&lt;/p&gt;
&lt;h2 id=&#34;claude-code-也在处理同一件事&#34;&gt;Claude Code 也在处理同一件事
&lt;/h2&gt;&lt;p&gt;Claude Code 也有 &lt;code&gt;/goal&lt;/code&gt;，官方文档的说法更直白：用户设置 completion condition，Claude 会跨 turn 持续工作，直到条件满足。&lt;/p&gt;
&lt;p&gt;Claude Code 文档还提到，每一轮结束后会检查完成条件是否成立；如果不成立，就继续下一轮。这一点很关键，因为它把“继续”从模型自己的主观收尾里拿出来，变成一个额外的条件判断。&lt;/p&gt;
&lt;p&gt;两家的具体实现细节不必强行等同，但方向是一致的：终端 agent 开始从“执行下一条指令”，走向“围绕可验证目标持续推进”。&lt;/p&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;code&gt;/goal&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;&lt;code&gt;/loop&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;hooks&lt;/td&gt;
					&lt;td&gt;在固定事件上自动执行规则&lt;/td&gt;
					&lt;td&gt;格式化、测试、通知、日志&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&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;记忆/项目说明文件&lt;/td&gt;
					&lt;td&gt;固化长期约束和仓库规则&lt;/td&gt;
					&lt;td&gt;团队规范、代码风格、工具入口&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这张表里，&lt;code&gt;goal&lt;/code&gt; 的位置很清楚：它不是替代 hooks，也不是替代记忆。它管的是“这次任务做到什么程度算完成”。&lt;/p&gt;
&lt;h2 id=&#34;好-goal-要写得像验收条件&#34;&gt;好 goal 要写得像验收条件
&lt;/h2&gt;&lt;p&gt;我现在会把一个 goal 写成四行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;目标：最终要出现什么用户可见结果。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;边界：哪些文件、接口、数据、视觉或行为不能乱动。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;验证：用哪些命令、测试、截图、评测或人工检查作为证据。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;停机：全部满足时停止；遇到哪些权限、事实、产品判断时暂停。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这和普通 prompt 的区别很大。&lt;/p&gt;
&lt;p&gt;普通 prompt 更像下一步动作，goal 更像完成口径。它不是让人从流程里消失，而是把人的判断提前放进去。你不再每一轮提醒它“这个不算完成”，而是在一开始就把“什么算完成”写成后续必须服从的条件。&lt;/p&gt;
&lt;p&gt;所以，越想让 agent 自己跑，越要把目标写窄。&lt;/p&gt;
&lt;p&gt;越想少盯过程，越要把验证写实。&lt;/p&gt;
&lt;p&gt;越不想它跑偏，越要把边界写清楚。&lt;/p&gt;
&lt;p&gt;这才是 &lt;code&gt;goal&lt;/code&gt; 真正值得关注的地方。不是多一条命令，不是可以跑多久，而是终端 agent 开始把“谁来判断做完”这件事抬到台面上。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.openai.com/codex/use-cases/follow-goals&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Follow a goal | Codex use cases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.openai.com/codex/cli/slash-commands&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Slash commands in Codex CLI | OpenAI Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.openai.com/blog/run-long-horizon-tasks-with-codex&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Run long horizon tasks with Codex | OpenAI Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://code.claude.com/docs/en/goal&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Keep Claude working toward a goal | Claude Code Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 详解 codex 新出的 goal 命令，工作原理是什么，为什么持续工作很长的时间，官方提供的案例是什么。claude code 有没有类似的命名，顺带整理近期 两家终端新出的，好用的、受欢迎的功能，整理为表格。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;保留原文主判断：&lt;code&gt;goal&lt;/code&gt; 的核心不是命令名，而是完成条件。&lt;/li&gt;
&lt;li&gt;补回原始提示词里要求的 Claude Code 对比和终端功能表格。&lt;/li&gt;
&lt;li&gt;压掉“官方文档复述”，把重点放在如何写一个可用的 goal。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>ChatGPT 之后英伟达 GPU 的两条路线</title>
        <link>https://ttf248.life/p/nvidia-data-center-gpu-since-chatgpt/</link>
        <pubDate>Fri, 15 May 2026 19:58:51 +0800</pubDate>
        
        <guid>https://ttf248.life/p/nvidia-data-center-gpu-since-chatgpt/</guid>
        <description>&lt;p&gt;整理 ChatGPT 以来的英伟达数据中心 GPU，不能只做型号清单。真正的变化是两条路线同时加速：一条追最高训练性能，一条追可卖、可交付、可合规的市场覆盖。&lt;/p&gt;
&lt;p&gt;训练路线看的是 H100、H200、B200、GB200 这类旗舰产品。它们不断把算力、显存、带宽、互联和整柜方案往上推。大模型越大，单卡性能已经不够，系统级连接、集群效率和能耗控制变得更重要。&lt;/p&gt;
&lt;p&gt;另一条路线是特供和区域合规版本。给中国市场的产品，不只是简单“阉割版”，而是在出口管制限制下，重新平衡性能、带宽、互联和可销售性。它们的意义不是代表最高技术，而是说明英伟达要在规则缝隙里保住客户和生态。&lt;/p&gt;
&lt;h2 id=&#34;先看全球主线h100-起跑h200-补课b200-开始卖平台&#34;&gt;先看全球主线：H100 起跑，H200 补课，B200 开始卖平台
&lt;/h2&gt;&lt;p&gt;这条线只看大模型训练和核心推理主线：A100 作为基线，H100、H200、B200、B300 依次往后排。&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;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;A100 80GB&lt;/td&gt;
					&lt;td&gt;2020，作为基线&lt;/td&gt;
					&lt;td&gt;Ampere&lt;/td&gt;
					&lt;td&gt;80GB HBM2e&lt;/td&gt;
					&lt;td&gt;2.039 TB/s&lt;/td&gt;
					&lt;td&gt;NVLink 600 GB/s&lt;/td&gt;
					&lt;td&gt;ChatGPT 前的大模型基准卡&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H100 SXM&lt;/td&gt;
					&lt;td&gt;2022-03-22&lt;/td&gt;
					&lt;td&gt;Hopper&lt;/td&gt;
					&lt;td&gt;80GB HBM3&lt;/td&gt;
					&lt;td&gt;3.35 TB/s&lt;/td&gt;
					&lt;td&gt;NVLink 900 GB/s&lt;/td&gt;
					&lt;td&gt;真正把生成式 AI 训练卡时代推开的产品&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H200 SXM&lt;/td&gt;
					&lt;td&gt;2023-11-13&lt;/td&gt;
					&lt;td&gt;Hopper 刷新版&lt;/td&gt;
					&lt;td&gt;141GB HBM3e&lt;/td&gt;
					&lt;td&gt;4.8 TB/s&lt;/td&gt;
					&lt;td&gt;NVLink 900 GB/s&lt;/td&gt;
					&lt;td&gt;重点不是 core 大跃迁，而是显存容量和带宽补课&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;B200 SXM&lt;/td&gt;
					&lt;td&gt;2024-03-18&lt;/td&gt;
					&lt;td&gt;Blackwell&lt;/td&gt;
					&lt;td&gt;180GB HBM3e&lt;/td&gt;
					&lt;td&gt;最高 8 TB/s&lt;/td&gt;
					&lt;td&gt;单 GPU NVLink 1.8 TB/s&lt;/td&gt;
					&lt;td&gt;重点转向平台级训练与推理吞吐&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;B300 / Blackwell Ultra&lt;/td&gt;
					&lt;td&gt;2025-03-18&lt;/td&gt;
					&lt;td&gt;Blackwell Ultra&lt;/td&gt;
					&lt;td&gt;288GB HBM3e&lt;/td&gt;
					&lt;td&gt;最高 8 TB/s&lt;/td&gt;
					&lt;td&gt;延续 rack-scale 互连路线&lt;/td&gt;
					&lt;td&gt;更明确地往 reasoning 和 test-time scaling 推进&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;如果只想记一个判断，大概可以这么压缩：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;H100 是“训练卡暴力拉升”的一代。&lt;/li&gt;
&lt;li&gt;H200 是“显存补课”的一代。&lt;/li&gt;
&lt;li&gt;B200 是“AI 工厂平台化”的一代。&lt;/li&gt;
&lt;li&gt;B300 是把 Blackwell 更明确推向 reasoning 时代的一代。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，英伟达卖的东西其实在变。&lt;/p&gt;
&lt;p&gt;H100 还很像“这张卡有多猛”。到了 Blackwell，英伟达卖的已经越来越像“这套平台能把多大的模型，以什么吞吐和什么功耗跑起来”。这就是为什么 B200、B300 的叙事里，单卡 TFLOPS 反而没以前那么像绝对主角。&lt;/p&gt;
&lt;h2 id=&#34;h200-为什么不是算力暴力翻倍卡&#34;&gt;H200 为什么不是“算力暴力翻倍卡”
&lt;/h2&gt;&lt;p&gt;很多人第一次看 H200，会本能地去找“它比 H100 快了多少”。&lt;/p&gt;
&lt;p&gt;但 H200 真正要解决的，不是 dense 算力数字不够好看，而是大模型一旦进入超长上下文、巨型 KV cache、MoE 和更大 batch 之后，瓶颈越来越像显存和显存带宽，而不是单纯的 BF16 峰值。&lt;/p&gt;
&lt;p&gt;所以 H200 的升级重点是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显存从 80GB 拉到 141GB；&lt;/li&gt;
&lt;li&gt;带宽从 3.35 TB/s 提到 4.8 TB/s；&lt;/li&gt;
&lt;li&gt;官方直接用 Llama 2 70B、GPT-3 175B 这类推理 workload 去强调收益。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它更像 Hopper 时代的一次补课，而不是全维度换代。&lt;/p&gt;
&lt;h2 id=&#34;再看中国特供线它的目标函数从来不是旗舰最强&#34;&gt;再看中国特供线：它的目标函数从来不是“旗舰最强”
&lt;/h2&gt;&lt;p&gt;中国特供线一定要单独看。&lt;/p&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;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;A800&lt;/td&gt;
					&lt;td&gt;2022-11&lt;/td&gt;
					&lt;td&gt;A100&lt;/td&gt;
					&lt;td&gt;80GB HBM2e；公开报道称互连较 A100 下调&lt;/td&gt;
					&lt;td&gt;互连&lt;/td&gt;
					&lt;td&gt;本地算力接近，但大规模扩展能力被压&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H800&lt;/td&gt;
					&lt;td&gt;2023-03&lt;/td&gt;
					&lt;td&gt;H100&lt;/td&gt;
					&lt;td&gt;公开报道普遍认为本地 HBM 能力仍接近 H100 级别&lt;/td&gt;
					&lt;td&gt;互连&lt;/td&gt;
					&lt;td&gt;典型的“单卡不至于太差，横向扩展受限”&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H20&lt;/td&gt;
					&lt;td&gt;2024-02 预售&lt;/td&gt;
					&lt;td&gt;Hopper 中国合规版&lt;/td&gt;
					&lt;td&gt;96GB HBM3、约 4.0 TB/s；公开渠道价约 1.2-1.5 万美元/卡&lt;/td&gt;
					&lt;td&gt;计算能力与其他受限项继续压低&lt;/td&gt;
					&lt;td&gt;不是 H800 的自然升级版，而是更严规则下的折中产物&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这张表里最值得记住的一句话是：&lt;/p&gt;
&lt;p&gt;A800 和 H800 更像“削互连”，H20 则连计算能力都继续往下压。&lt;/p&gt;
&lt;p&gt;所以如果只看显存，会很容易误判。H20 比 H800 更新，不代表它按全球旗舰口径一定更强；它的商业意义首先是满足更严格规则之后还能卖，其次才是尽量保留一些可用性。&lt;/p&gt;
&lt;h2 id=&#34;哪些升级可以直接算哪些不能硬算&#34;&gt;哪些升级可以直接算，哪些不能硬算
&lt;/h2&gt;&lt;p&gt;这一点最好先讲清楚，不然后面的百分比都容易变成假精确。&lt;/p&gt;
&lt;p&gt;最稳的计算方式是：&lt;/p&gt;
\[
\text{升级率}=\frac{\text{新指标}-\text{旧指标}}{\text{旧指标}}
\]&lt;p&gt;但这个公式只适合口径一致的指标，比如显存容量、显存带宽、NVLink 带宽。它不适合把平台级推理吞吐、整机 token 产出和单卡 TFLOPS 粗暴揉成一条线。&lt;/p&gt;
&lt;h3 id=&#34;全球主线里能直接算的几组数字&#34;&gt;全球主线里，能直接算的几组数字
&lt;/h3&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;A100 80GB -&amp;gt; H100 SXM&lt;/td&gt;
					&lt;td&gt;带宽、互连、Tensor Core 峰值&lt;/td&gt;
					&lt;td&gt;显存带宽约 +64%，NVLink 约 +50%，BF16/FP16 Tensor Core 峰值约 +534%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H100 SXM -&amp;gt; H200 SXM&lt;/td&gt;
					&lt;td&gt;显存、带宽&lt;/td&gt;
					&lt;td&gt;显存约 +76%，带宽约 +43%，NVLink 基本不变&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H200 SXM -&amp;gt; B200 SXM&lt;/td&gt;
					&lt;td&gt;显存、带宽、互连&lt;/td&gt;
					&lt;td&gt;显存约 +28%，带宽约 +67%，单 GPU NVLink 翻倍到 1.8 TB/s&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;B200 -&amp;gt; B300&lt;/td&gt;
					&lt;td&gt;显存、平台级推理能力&lt;/td&gt;
					&lt;td&gt;显存约 +60%；平台能力更强调 FP4、reasoning 和 attention 吞吐&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;从这个角度看，代际节奏也很清楚：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;H100 主要把训练卡拉到一个新高度。&lt;/li&gt;
&lt;li&gt;H200 把显存短板补齐。&lt;/li&gt;
&lt;li&gt;B200 开始把“单卡升级”转成“平台升级”。&lt;/li&gt;
&lt;li&gt;B300 再往大规模推理和 reasoning 方向加码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;中国特供线里只能谨慎算一部分&#34;&gt;中国特供线里，只能谨慎算一部分
&lt;/h3&gt;&lt;p&gt;这里更要保守。&lt;/p&gt;
&lt;p&gt;因为 A800、H800、H20 的完整官方公开规格并不像全球主线那样整齐，很多关键参数主要来自 Reuters 报道和可追溯硬件媒体转述。&lt;/p&gt;
&lt;p&gt;能比较稳地说的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A800、H800 的主要削减方向都集中在互连；&lt;/li&gt;
&lt;li&gt;H20 显存比 H800 更大，但这不等于它在全球旗舰意义上“自然升级”；&lt;/li&gt;
&lt;li&gt;中国特供线每一代首先是在适应规则，而不是按性能最大化去延续旗舰路线。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这条线不能硬套成“每一代全面提升多少”。它不是那种代际关系。&lt;/p&gt;
&lt;h2 id=&#34;价格部分只能保留公开可追溯样本&#34;&gt;价格部分，只能保留公开可追溯样本
&lt;/h2&gt;&lt;p&gt;数据中心 GPU 的价格是最容易产生误导的部分。&lt;/p&gt;
&lt;p&gt;因为英伟达并不总是公开单卡 MSRP，公开世界里更常见的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DGX 整机官方起售价；&lt;/li&gt;
&lt;li&gt;第三方整机挂牌价；&lt;/li&gt;
&lt;li&gt;Reuters 或供应链转述的特供版渠道价。&lt;/li&gt;
&lt;/ul&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;DGX H100&lt;/td&gt;
					&lt;td&gt;2022 年发布时官方起售价 19.9 万美元&lt;/td&gt;
					&lt;td&gt;这是最干净的官方锚点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DGX B200&lt;/td&gt;
					&lt;td&gt;公开挂牌价大约比同阶段 DGX H100 高 40%-50%&lt;/td&gt;
					&lt;td&gt;说明全球旗舰平台确实继续涨价&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H20 单卡&lt;/td&gt;
					&lt;td&gt;Reuters 报道约 1.2 万到 1.5 万美元&lt;/td&gt;
					&lt;td&gt;这是渠道样本，不是全球旗舰口径&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H20 八卡服务器&lt;/td&gt;
					&lt;td&gt;公开报道约 140 万元人民币&lt;/td&gt;
					&lt;td&gt;不能拿来和 DGX 体系直接硬比，只能说明合规线的商业定位&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;所以“整体售价提升了多少”这个问题，稳妥的回答是两句：&lt;/p&gt;
&lt;p&gt;第一，全球旗舰主线确实在涨，而且涨得不小。&lt;/p&gt;
&lt;p&gt;第二，中国特供线未必一路更贵，甚至可能出现“更晚出的卡更便宜”的情况，因为它首先在追求合规可卖，而不是旗舰性能和定价不断上探。&lt;/p&gt;
&lt;h2 id=&#34;最后收一下&#34;&gt;最后收一下
&lt;/h2&gt;&lt;p&gt;如果把 ChatGPT 发布之后的数据中心 GPU 变化压成一句话，我现在会这么写：&lt;/p&gt;
&lt;p&gt;全球主线是 H100 起跑、H200 补显存、B200 平台化、B300 往 reasoning 时代再推一步；中国特供线则是 A800、H800、H20 在越来越严的规则里尽量保住可用性。&lt;/p&gt;
&lt;p&gt;这两条线不要混着看。&lt;/p&gt;
&lt;p&gt;混着看，最后很容易得出几个大差不差、但方向不对的结论。英伟达在全球卖的是训练与推理平台，在中国特供线卖的则首先是“还能交付、还能部署”的折中产品。目标函数不一样，升级逻辑当然也不一样。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://openai.com/index/chatgpt/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenAI：Introducing ChatGPT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.nvidia.com/en-us/data-center/h100/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA H100 GPU&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://nvidianews.nvidia.com/news/nvidia-announces-dgx-h100-systems-worlds-most-advanced-enterprise-ai-infrastructure&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA Announces DGX H100 Systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://nvidianews.nvidia.com/news/nvidia-supercharges-hopper-the-worlds-leading-ai-computing-platform&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA Supercharges Hopper With H200&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.nvidia.com/en-us/data-center/h200/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA H200 GPU&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://nvidianews.nvidia.com/news/nvidia-blackwell-platform-arrives-to-power-a-new-era-of-computing&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA Blackwell Platform Arrives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.nvidia.com/dgx/dgxb200-user-guide/introduction-to-dgxb200.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA DGX B200 User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://nvidianews.nvidia.com/news/nvidia-blackwell-ultra-ai-factory-platform-paves-way-for-age-of-ai-reasoning&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA Blackwell Ultra AI Factory Platform Paves Way for Age of AI Reasoning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.nvidia.com/en-us/data-center/dgx-b300/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;NVIDIA DGX B300&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.reuters.com/technology/nvidia-starts-making-new-chips-china-comply-with-new-us-limits-2022-11-08/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Reuters：Nvidia starts making new chips for China to comply with new U.S. limits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.reuters.com/technology/nvidia-tweaks-flagship-h100-chip-export-china-2023-03-21/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Reuters：Nvidia tweaks flagship H100 chip for export to China&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.reuters.com/technology/nvidias-new-china-focused-ai-chip-set-be-sold-similar-price-huawei-product-2024-02-01/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Reuters：Nvidia’s new China-focused AI chip set to be sold at similar price to Huawei product&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;整理自从 ChatGPT 发布以来，英伟达发布的显卡型号和对应的性能参数，前一代对比上一代升级了多少，整体售价提升了多少，我要的是数据中心用的显卡，包含给中国的特供版本。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这篇从上面的原始提示词出发，按第一次写稿的方式确定主线、材料密度和结构。&lt;code&gt;date&lt;/code&gt; 字段沿用原发布时间，其他内容只服务当前文章承诺。&lt;/p&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>AI 会写代码后，新人的练级路线变了</title>
        <link>https://ttf248.life/p/when-ai-writes-code-how-juniors-level-up/</link>
        <pubDate>Mon, 27 Apr 2026 23:18:36 +0800</pubDate>
        
        <guid>https://ttf248.life/p/when-ai-writes-code-how-juniors-level-up/</guid>
        <description>&lt;p&gt;这几个月用 Claude、Codex 写代码，我最强烈的感受不是“程序员要没了”。&lt;/p&gt;
&lt;p&gt;更准确的说法是，很多以前会丢给新人练手的活，现在 AI 能先打一版了。脚手架、小功能、测试、文档、边角 bug，以前这些活不算高级，但很适合把一个人从“会语法”磨到“知道工程怎么回事”。&lt;/p&gt;
&lt;p&gt;对老手来说，AI 是提效工具。对新人来说，它更像把新手村拆短了。门没有关，但从入口走到能独立负责事情，中间能慢慢摔跤的地方变少了。&lt;/p&gt;
&lt;h2 id=&#34;被压缩的是练手机会&#34;&gt;被压缩的是练手机会
&lt;/h2&gt;&lt;p&gt;以前团队带新人，很多时候靠的不是精心设计的培训，而是一堆低风险任务。&lt;/p&gt;
&lt;p&gt;改一个页面，接一个接口，补几个 CRUD，修一个日志里能看出来的小 bug。活不大，也不体面，但它们有价值。新人会在这些任务里学会：&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;li&gt;线上系统为什么和本地 Demo 不一样。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;现在最容易被 AI 吃掉的，恰好就是这类活。&lt;/p&gt;
&lt;p&gt;企业当然会算账。一个有经验的人带着 AI，先让模型出草稿，再人工审一遍，可能比安排新人慢慢磨更快。尤其在预算收紧、交付压力大的时候，“先不补初级岗”会变成一个很自然的选择。&lt;/p&gt;
&lt;p&gt;所以问题不只是裁员。更隐蔽的问题是，不再补人。&lt;/p&gt;
&lt;p&gt;岗位还在，需求也还在，但原来那条“从小活开始练”的路径变窄了。&lt;/p&gt;
&lt;h2 id=&#34;老手为什么更吃红利&#34;&gt;老手为什么更吃红利
&lt;/h2&gt;&lt;p&gt;AI 写代码以后，老手反而更容易受益，这件事看起来有点反直觉。&lt;/p&gt;
&lt;p&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;测试是不是只是在糊弄 CI；&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;p&gt;Anthropic、METR、BLS、ILO 这些报告从不同角度都在指向类似的现实：AI 会改变任务结构，但不是把整份工作一次性抹掉。软件开发仍然需要人，只是“重复性编码”和“低判断力执行”的价值变薄了。&lt;/p&gt;
&lt;p&gt;这对老手是红利。因为 AI 把体力活压短以后，老手可以把更多时间放到设计、审查、边界和验证上。&lt;/p&gt;
&lt;p&gt;但对新人就尴尬了。你本来需要靠那些体力活长出判断力，现在这些活先被 AI 接走了一半。公司又希望你一上来就会审 AI、会拆需求、会判断质量。&lt;/p&gt;
&lt;p&gt;这像是要求新人跳过训练关，直接开始打中段 boss。&lt;/p&gt;
&lt;h2 id=&#34;新人要练的东西变了&#34;&gt;新人要练的东西变了
&lt;/h2&gt;&lt;p&gt;我不觉得新入行的人没路了。&lt;/p&gt;
&lt;p&gt;软件需求还在，系统越来越多，业务也不会因为 AI 会写代码就自动变简单。真正变的是入场能力。&lt;/p&gt;
&lt;p&gt;以前新人最重要的是“能把需求写成代码”。以后可能要多两层：&lt;/p&gt;
&lt;p&gt;第一，能把需求拆成 AI 能做的小任务。&lt;/p&gt;
&lt;p&gt;不是一句“帮我做个后台”，而是能说清楚当前目标、边界、输入输出、不能动哪些文件、完成后怎么验证。&lt;/p&gt;
&lt;p&gt;第二，能审 AI 的结果。&lt;/p&gt;
&lt;p&gt;这比写 prompt 难。审代码需要知道项目上下文，知道语言和框架的坑，也知道业务里哪些东西不能省。你要能指出“不对在哪里”，而不是只说“感觉不太好”。&lt;/p&gt;
&lt;p&gt;所以新人不能只练“让 AI 写”。还要练：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自己从零写一遍小功能；&lt;/li&gt;
&lt;li&gt;读懂 AI 写的每一行；&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;AI 可以加速，但不能替你完成这部分判断力训练。&lt;/p&gt;
&lt;h2 id=&#34;新手村还在只是要自己搭&#34;&gt;新手村还在，只是要自己搭
&lt;/h2&gt;&lt;p&gt;如果我是刚入行的人，我会刻意给自己留一些“低效率练习”。&lt;/p&gt;
&lt;p&gt;比如同一个小功能，先不用 AI 手写一版，再让 AI 写一版，对比差异。或者让 AI 写完以后，自己逐行解释，解释不出来就当作没学会。再或者故意把任务拆得很小，每轮只允许模型过一个检查点，不让它一口气把项目铺满。&lt;/p&gt;
&lt;p&gt;这听起来比“AI 帮我全写了”慢。&lt;/p&gt;
&lt;p&gt;但新人真正缺的不是代码产量，而是判断力的来源。你不亲自经历几个“看起来能跑，后来发现设计错了”的坑，就很难知道 AI 什么时候也在犯同样的错。&lt;/p&gt;
&lt;p&gt;老一辈所谓“古法编程”的价值，不是怀旧。它只是让人见过足够多的错误形态。见过以后，AI 给你一段代码，你才知道哪些地方要追问，哪些地方要加测试，哪些地方不能轻易信。&lt;/p&gt;
&lt;p&gt;所以新人不是不能用 AI，而是不能只用 AI。&lt;/p&gt;
&lt;p&gt;门没有关。只是以前团队和项目自然提供的新手村，正在被效率工具压缩。以后想进门的人，可能得自己搭一部分训练场。&lt;/p&gt;
&lt;p&gt;这不公平，但很现实。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.weforum.org/reports/the-future-of-jobs-report-2025/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;The Future of Jobs Report 2025&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.bls.gov/ooh/computer-and-information-technology/software-developers.htm&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Software Developers, Quality Assurance Analysts, and Testers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.bls.gov/ooh/computer-and-information-technology/computer-programmers.htm&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Computer Programmers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.bls.gov/opub/mlr/2025/article/ai-impacts-in-bls-employment-projections.htm&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;AI impacts in BLS employment projections&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.anthropic.com/research/anthropic-economic-index-january-2026-report&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Anthropic Economic Index: Insights from the Latest Data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.anthropic.com/research/economic-index-primitives&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Anthropic Economic Index: Tracking AI Use Across Work and Life&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.ilo.org/publications/generative-ai-and-jobs-refined-global-index-occupational-exposure&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Generative AI and Jobs: A Refined Global Index of Occupational Exposure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://siepr.stanford.edu/publications/working-paper/generative-ai-work&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Generative AI at Work&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;AI 编程的一些思考，刚毕业的应届生，编码能力肯定是不如 claude、codex，对于老手，AI 能提效，但是 AI 导致企业大幅削减了初级程序员的岗位，本人毕业已经十年，经历了新人到老人的爬坑阶段，以后，但是新入行的呢，他们会怎么样？老一辈算是经历过古法编程，说白了，能识别出来AI写的是好的，还是不好的，毕竟当前阶段的AI还是会犯错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;保留原文核心问题：AI 提效老手，但压缩新人练手机会。&lt;/li&gt;
&lt;li&gt;把旧稿中过密的报告材料降级为支撑，把主线放回“新手村变窄”。&lt;/li&gt;
&lt;li&gt;不写求职鸡汤，只给出新人需要主动搭训练场的判断。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>换到联通之后，美国节点不再是默认最优</title>
        <link>https://ttf248.life/p/us-nodes-not-as-good-after-switching-to-china-unicom/</link>
        <pubDate>Thu, 16 Apr 2026 01:56:14 +0800</pubDate>
        
        <guid>https://ttf248.life/p/us-nodes-not-as-good-after-switching-to-china-unicom/</guid>
        <description>&lt;p&gt;搬家以后，家里的宽带从电信换成了联通。刷剧、打游戏都没什么明显变化，我一开始也没太当回事。&lt;/p&gt;
&lt;p&gt;真正露馅是在下载资料的时候。我习惯性切到美国节点，按以前的经验，美国机房带宽足，下载大文件通常能跑得很满。结果这次怎么切都上不去，反而新加坡、台湾节点顺了很多。&lt;/p&gt;
&lt;p&gt;这一下把我原来那个很粗的判断拆开了：节点快不快，不能只看服务器在哪个国家，也不能只看机房带宽。你家宽带站在哪张网、代理服务商买了什么上游、回程更偏哪家运营商，最后都会落到同一个下载进度条上。&lt;/p&gt;
&lt;h2 id=&#34;日常使用会把差异藏起来&#34;&gt;日常使用会把差异藏起来
&lt;/h2&gt;&lt;p&gt;这类问题平时不容易发现。&lt;/p&gt;
&lt;p&gt;看剧有平台自己的 CDN 和调度，很多流量不需要一路跨境拉回来。打游戏更敏感的是延迟、抖动和稳定性，不一定会持续把带宽跑满。只有下载大文件这种场景，才会把链路的上限逼出来。&lt;/p&gt;
&lt;p&gt;所以这次的体感很直接：不是联通不能用，也不是美国节点突然都变差了，而是我原来那套“美国机房资源足，所以默认选美国”的经验，换了一条家宽之后不再成立。&lt;/p&gt;
&lt;p&gt;以前用电信时，美国节点能打满，我容易把功劳都归给美国机房。现在回头看，那更像是几件事刚好叠在一起：美国服务器资源够、节点线路对电信友好、回程也没有明显卡住。换到联通以后，同一个选择就不一定占便宜了。&lt;/p&gt;
&lt;h2 id=&#34;关键不是有没有国际网络而是你走到哪条路上&#34;&gt;关键不是有没有国际网络，而是你走到哪条路上
&lt;/h2&gt;&lt;p&gt;原始提示词里提到一个判断：&lt;code&gt;CN2&lt;/code&gt; 是电信的，所以联通要租电信网络。这个说法只能算抓到了一部分。&lt;/p&gt;
&lt;p&gt;电信确实有自己的 ChinaNet 和 &lt;code&gt;CN2 (AS4809)&lt;/code&gt;，很多跨境线路商也会把 &lt;code&gt;CN2&lt;/code&gt; 当卖点。它通常意味着更重视质量的承载网络，尤其在面向国际业务的时候很容易被拿来比较。&lt;/p&gt;
&lt;p&gt;但联通并不是没有自己的国际网络。联通国际公开资料里写到 &lt;code&gt;AS4837&lt;/code&gt;，也提到更偏高质量承载的 &lt;code&gt;AS9929&lt;/code&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;li&gt;这个节点是不是只在某一家运营商下表现好。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以前我在电信下用得舒服，可能只是刚好站在了那条线路更偏爱的入口。换到联通后，入口变了，美国节点的优势也就没那么稳了。&lt;/p&gt;
&lt;h2 id=&#34;新加坡和台湾变顺不代表它们天然更强&#34;&gt;新加坡和台湾变顺，不代表它们天然更强
&lt;/h2&gt;&lt;p&gt;更有意思的是，美国节点掉速之后，新加坡和台湾节点反而更顺。&lt;/p&gt;
&lt;p&gt;这不说明亚洲机房一定比美国强，也不说明联通用户以后都该默认选亚洲节点。更合理的解释是：亚洲方向距离更近，区域互联更密，我现在这条联通宽带和这些节点的线路组合更合拍。&lt;/p&gt;
&lt;p&gt;节点体验其实像一条完整回家路线。机房所在地只是其中一段，国内入口、国际出口、回程策略和高峰拥塞都要一起算。美国机房再强，如果回程绕、上游偏、或者高峰期堵，最后还是跑不起来。&lt;/p&gt;
&lt;p&gt;所以后来我选节点的顺序变了。&lt;/p&gt;
&lt;p&gt;以前是先看国家：美国优先，新加坡、台湾备选。现在是先看线路匹配：我家是什么宽带，这个服务商在哪些地区对这家运营商友好，实际下载能不能稳定跑起来。&lt;/p&gt;
&lt;h2 id=&#34;以后买节点先别急着看国家&#34;&gt;以后买节点，先别急着看国家
&lt;/h2&gt;&lt;p&gt;这次换宽带给我的修正很实用。&lt;/p&gt;
&lt;p&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;li&gt;如果服务商标了线路类型，再看它更偏哪家运营商。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我现在不太相信“某国节点一定快”这种说法了。更准确的说法是：某个节点，在某家宽带、某个时间段、某条回程下，可能很快。&lt;/p&gt;
&lt;p&gt;这个判断听起来麻烦，但比盲选美国节点靠谱。家宽一换，原来的经验就可能过期。真正诚实的不是节点介绍页，而是你自己那条下载曲线。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.ctamericas.com/wp-content/uploads/2018/10/IP-Access.pdf&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;China Telecom Americas: IP Access / Global Internet Service（CN2、ChinaNet 介绍）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sg.chinaunicomglobal.com/products-dia&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;China Unicom Global Singapore: DIA（AS4837、AS9929 介绍）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://eu.chinaunicomglobal.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;China Unicom Global Europe: Global Presence（新加坡、台湾、美国等海外节点）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;提示词：中国内地，走代理软件，科学上网，发现联通、电信宽带的区别了，日常看剧、打游戏都很难发现。偶尔下载资料，喜欢选择美国节点，那边的节点，下载速度基本能打满，这样也符合以前的认知，美国机房的服务器，宽带资源给的很足。近期搬家换到了联通的带宽发现了问题，美国节点的下载速度上不去了，切换到新加坡、台湾倒是好了很多，这和中国宽带的骨干网络有关系，CN2高速骨干网络，都是电信的，联通要租用电信的网络。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;保留“搬家换宽带后才发现差异”的真实触发点，不写成运营商百科。&lt;/li&gt;
&lt;li&gt;修正“联通只能租电信网络”的绝对说法，改成线路匹配、去程回程和服务商上游共同决定体验。&lt;/li&gt;
&lt;li&gt;把结论落到节点选择方法：先测自己家宽下的实际表现，再谈美国、亚洲或机房资源。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Hermes 和 OpenClaw 的 token 账单差在哪</title>
        <link>https://ttf248.life/p/hermes-openclaw-token-usage-diff/</link>
        <pubDate>Thu, 16 Apr 2026 01:13:53 +0800</pubDate>
        
        <guid>https://ttf248.life/p/hermes-openclaw-token-usage-diff/</guid>
        <description>&lt;p&gt;写完上一篇 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/hermes-openclaw-not-the-same-game/&#34; &gt;Hermes 和 OpenClaw 的对比&lt;/a&gt;以后，我又去翻了一遍两边文档。越看越觉得，如果只问功能像不像，很容易看偏。&lt;/p&gt;
&lt;p&gt;更直接的问题是：它们把 token 花在了哪里。&lt;/p&gt;
&lt;p&gt;OpenClaw 更像一个长期在线的助理工作台，默认就要带身份、工作区、消息面和记忆边界。Hermes 更像一个本地 agent 内核，默认先把上下文压住，需要时再发现、再注入、再展开。一个把成本前置，一个把成本后置。最后账单长得当然不一样。&lt;/p&gt;
&lt;h2 id=&#34;openclaw-的固定盘更厚&#34;&gt;OpenClaw 的固定盘更厚
&lt;/h2&gt;&lt;p&gt;OpenClaw 的出发点，不是“启动一个轻量 CLI agent 先干活”，而是“让一个助理在工作区里长期存在”。&lt;/p&gt;
&lt;p&gt;它的 workspace 文档里，&lt;code&gt;AGENTS.md&lt;/code&gt;、&lt;code&gt;SOUL.md&lt;/code&gt;、&lt;code&gt;USER.md&lt;/code&gt; 这类文件会围绕 session 工作。&lt;code&gt;IDENTITY.md&lt;/code&gt;、&lt;code&gt;TOOLS.md&lt;/code&gt;、&lt;code&gt;HEARTBEAT.md&lt;/code&gt;、&lt;code&gt;BOOT.md&lt;/code&gt;、&lt;code&gt;MEMORY.md&lt;/code&gt; 也都在同一套助理环境里。文档还专门提醒 &lt;code&gt;HEARTBEAT.md&lt;/code&gt; 要短，避免 token burn。&lt;/p&gt;
&lt;p&gt;这句话其实很说明问题。OpenClaw 不是不知道 token 贵，而是它的产品目标决定了很多上下文必须提前在场。&lt;/p&gt;
&lt;p&gt;你要一个 agent 同时面对 Telegram、Discord、Slack、WhatsApp，还要知道自己是谁、该怎么说话、该怎么路由任务、哪些记忆能用、哪些工具能开，这些规则就不能每次临时拼。&lt;/p&gt;
&lt;p&gt;所以 OpenClaw 的 token 消耗更像一张“长期在线助理”的账：&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;li&gt;一部分工具和技能索引也要能被看见。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它不是不会省。OpenClaw 的 token 文档也写了，skills 默认进系统提示词的是 metadata，具体内容按需读。问题在于，它的底座本来就更像一个完整助理环境。这个底座好用，但不会特别轻。&lt;/p&gt;
&lt;h2 id=&#34;hermes-的默认姿势更克制&#34;&gt;Hermes 的默认姿势更克制
&lt;/h2&gt;&lt;p&gt;Hermes 的文档里反复出现两个思路：按需加载，保住 prompt cache。&lt;/p&gt;
&lt;p&gt;它处理 context files 的方式很典型。启动时只拿当前目录命中的项目上下文，像 &lt;code&gt;AGENTS.md&lt;/code&gt;、&lt;code&gt;CLAUDE.md&lt;/code&gt;、&lt;code&gt;.cursorrules&lt;/code&gt; 这类文件是 first match wins，不是一口气全塞进系统提示词。子目录里的上下文，也不是一开始全文加载，而是等 agent 真的走到相关路径、读到相关文件，再逐步发现。&lt;/p&gt;
&lt;p&gt;这和 OpenClaw 的气质完全不一样。&lt;/p&gt;
&lt;p&gt;Hermes 更像是在说：系统提示词最好别乱长。能后置的就后置，能按需出现的就按需出现。skill 也是同一套 progressive disclosure 逻辑，先给模型一个轻量索引，需要的时候再打开具体说明。&lt;/p&gt;
&lt;p&gt;它的记忆设计也更像组合件。&lt;code&gt;SOUL.md&lt;/code&gt;、&lt;code&gt;USER.md&lt;/code&gt;、内置 memory 有固定边界，session search、memory provider、Honcho 这些再按需要叠上去。这个设计不保证所有任务都省 token，但它让默认路径更不容易膨胀。&lt;/p&gt;
&lt;p&gt;所以我会把 Hermes 归成另一种账：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开局固定上下文更克制；&lt;/li&gt;
&lt;li&gt;项目上下文按路径发现；&lt;/li&gt;
&lt;li&gt;skill 按需展开；&lt;/li&gt;
&lt;li&gt;工具输出尽量只回关键结果；&lt;/li&gt;
&lt;li&gt;真复杂时再把 memory、sub-agent、长工具结果叠上去。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它不是“天然便宜”，而是更容易从轻状态开始。&lt;/p&gt;
&lt;h2 id=&#34;谁更省要看你在养什么&#34;&gt;谁更省，要看你在养什么
&lt;/h2&gt;&lt;p&gt;很多工具对比最后都会落成一句“谁更省 token”。这个说法太粗。&lt;/p&gt;
&lt;p&gt;如果你要的是长期在线、跨平台、有身份感、能维持多消息面关系的助理，OpenClaw 把上下文前置是合理代价。你不能既要它像一个一直在场的人，又要求每轮都像一次性 CLI 小工具那么轻。&lt;/p&gt;
&lt;p&gt;如果你主要在本地仓库里做代码、技能、上下文发现和工具执行，Hermes 的后置加载会更顺手。它把“现在不用的东西先别进上下文”当成默认设计，token 账就更容易控。&lt;/p&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;OpenClaw&lt;/th&gt;
					&lt;th&gt;Hermes&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;本地 agent 内核&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&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;优势&lt;/td&gt;
					&lt;td&gt;存在感稳定，多平台边界清楚&lt;/td&gt;
					&lt;td&gt;起步轻，prompt cache 更容易保住&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;风险&lt;/td&gt;
					&lt;td&gt;固定盘容易变厚&lt;/td&gt;
					&lt;td&gt;复杂任务展开后一样会烧 token&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;如果只看默认架构，我会倾向于说 Hermes 更容易把 token 压住。但如果任务目标是“养一个在线助理”，那 OpenClaw 多花的 token 不一定是浪费，可能就是它要交付的产品体验。&lt;/p&gt;
&lt;h2 id=&#34;真要算账别靠体感&#34;&gt;真要算账，别靠体感
&lt;/h2&gt;&lt;p&gt;还有一点比架构名字更重要：实际账单要看工具给出的上下文和用量。&lt;/p&gt;
&lt;p&gt;OpenClaw 有 &lt;code&gt;/context detail&lt;/code&gt;、&lt;code&gt;/usage tokens&lt;/code&gt; 这类命令，适合看每次 session 到底带了什么。Hermes 也可以结合 context 注入、skills 展开、Honcho 或 provider 账单去看实际消耗。&lt;/p&gt;
&lt;p&gt;光看文档只能判断倾向，不能替你算最终成本。真正决定账单的，是你写了多长的 &lt;code&gt;SOUL.md&lt;/code&gt;，塞了多少 memory，开了多少 skill，工具输出有没有收住，模型本身贵不贵。&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;如果 skill 和 memory 全开，那是能力扩张；&lt;/li&gt;
&lt;li&gt;如果子代理各自复制大量上下文，那是并行成本；&lt;/li&gt;
&lt;li&gt;如果 prompt 前缀稳定，缓存命中才有意义。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以比较 Hermes 和 OpenClaw，不该只问“谁省”。应该问：我的任务是不是需要一个长期在线助理，还是只需要一个能在本地仓库里按需展开的 agent。&lt;/p&gt;
&lt;p&gt;这个问题答清楚，token 账就清楚了一半。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/hermes-openclaw-not-the-same-game/&#34; &gt;前一篇：把 Hermes 当成 OpenClaw 替代品，可能一开始就看偏了&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/overview&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Features Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/context-files/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Context Files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/personality/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Personality &amp;amp; SOUL.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/skills&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Skills&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/reference/tools-reference/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Built-in Tools Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/honcho/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Honcho Memory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/concepts/agent-workspace&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Agent Workspace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/reference/token-use&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Token Use and Costs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/concepts/memory&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Memory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/multi-agent&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Multi-Agent Routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/reference/context&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Context Reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;提示词：Hermes 和 OpenClaw 再写一篇文章，关于两者 token 消耗的情况，架构不同，消耗必然不一样&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;保留“架构不同，token 消耗必然不同”的原始判断。&lt;/li&gt;
&lt;li&gt;把旧稿里过密的文档摘录改成账单结构比较：固定盘、按需展开、工具输出和缓存。&lt;/li&gt;
&lt;li&gt;不写成“谁更省”的绝对结论，而是落到使用场景：长期在线助理和本地 agent 内核不是同一种成本模型。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Hermes 不能按 OpenClaw 平替来估</title>
        <link>https://ttf248.life/p/hermes-openclaw-not-the-same-game/</link>
        <pubDate>Wed, 15 Apr 2026 23:52:20 +0800</pubDate>
        
        <guid>https://ttf248.life/p/hermes-openclaw-not-the-same-game/</guid>
        <description>&lt;p&gt;这两天我把 Hermes 和 OpenClaw 的文档来回翻了一圈，越看越觉得，很多人把这两个项目放在一起比，其实一开始就比偏了。&lt;/p&gt;
&lt;p&gt;它们当然都在做“个人 AI 助手”。都能接消息、调模型、跑工具、留住一点上下文。Hermes 甚至还专门做了 &lt;code&gt;hermes claw migrate&lt;/code&gt;，摆明了是知道自己会接到一批 OpenClaw 用户。&lt;/p&gt;
&lt;p&gt;但是说白了，Hermes 不是换皮版 OpenClaw，OpenClaw 也不是多了几个消息入口的 agent framework。一个是从 &lt;code&gt;Gateway&lt;/code&gt; 往外长，一个是从 &lt;code&gt;AIAgent&lt;/code&gt; 往外长。这个差别不先看明白，后面谈架构、设计理念和生态，基本都会越聊越乱。&lt;/p&gt;
&lt;h2 id=&#34;它们一开始就没打算长成一个东西&#34;&gt;它们一开始就没打算长成一个东西
&lt;/h2&gt;&lt;p&gt;OpenClaw 官方文档把 &lt;code&gt;Gateway&lt;/code&gt; 放在非常前的位置。它的定义很直接：一个长期运行的 Gateway 持有所有消息面，控制面客户端、Web UI、Node 设备都通过 WebSocket 连到这个 Gateway 上，而且一个 host 上只有一个 Gateway 去管核心消息连接，像 WhatsApp 这种会话也明确由它独占。&lt;/p&gt;
&lt;p&gt;这意味着什么？意味着 OpenClaw 的世界观不是“先有一个 agent，再给它接几个入口”，而是“先有一个常驻的通信和控制平面，再把 agent runtime、session、skills、delegate、sub-agent 都组织到这个平面里”。&lt;/p&gt;
&lt;p&gt;Hermes 就不是这个味道。它的架构图一上来写得很明白，CLI、Gateway、ACP、Batch Runner、API Server、Python Library 这些入口，最后都汇到同一个 &lt;code&gt;AIAgent&lt;/code&gt; 核心。Session storage 是 SQLite + FTS5，工具后端、provider resolution、prompt builder 都是围着这个 core 组织的。&lt;/p&gt;
&lt;p&gt;所以从骨架上看，OpenClaw 更像一个“常驻型个人助理系统”；Hermes 更像一个“agent runtime 平台”，消息入口只是它的一个延伸面。&lt;/p&gt;
&lt;h2 id=&#34;一个把-gateway-当主角一个把-agent-当主角&#34;&gt;一个把 Gateway 当主角，一个把 Agent 当主角
&lt;/h2&gt;&lt;p&gt;这个架构差异，最后会直接变成使用体验差异。&lt;/p&gt;
&lt;p&gt;OpenClaw 的强项，是把“我怎么在很多渠道上拥有一个长期在线、带身份、带边界、可路由的助手”这件事做得非常重。它有默认的 DM pairing、安全策略、按 channel/account/peer 做 routing、多 agent 绑定、delegate 模式，甚至连“代表谁发、用哪个账号收、在哪个群里出现”这种组织级问题都专门写成文档了。&lt;/p&gt;
&lt;p&gt;换句话说，OpenClaw 想先解决的是存在性问题。这个 agent 先得像个真的人，或者像个真的组织助理，活在 Telegram、Discord、Slack、WhatsApp、Signal、WebChat 这些面上，然后你再去谈它会不会写代码、会不会做任务分发。&lt;/p&gt;
&lt;p&gt;Hermes 的重心更偏“能力积累”。它当然也有 Gateway，也能接 Telegram、Discord、Slack、WhatsApp 这些平台，但官方叙事里最靠前的词不是路由和账号，而是 persistent memory、skills、MCP、sub-agents、toolsets，还有 batch processing、trajectory export、RL training。&lt;/p&gt;
&lt;p&gt;这就很关键了。Hermes 要解决的核心问题不是“怎么把一个 AI 接到尽可能多的消息面上”，而是“怎么让这个 agent 逐步长出自己的技能、记忆和可复用能力，甚至还能顺手拿去做训练数据和实验”。&lt;/p&gt;
&lt;p&gt;所以我更愿意这么概括：&lt;/p&gt;
&lt;p&gt;OpenClaw 是 communication-first 的 personal assistant system。&lt;/p&gt;
&lt;p&gt;Hermes 是 agent-core-first 的 self-hosted agent platform。&lt;/p&gt;
&lt;h2 id=&#34;记忆技能和扩展方式也不是一个味道&#34;&gt;记忆、技能和扩展方式，也不是一个味道
&lt;/h2&gt;&lt;p&gt;很多人会说，这俩不都支持 skill、memory、plugin 吗。没错，都支持。但是落点不一样。&lt;/p&gt;
&lt;p&gt;OpenClaw 的 agent runtime 里，workspace 是很重的概念。&lt;code&gt;AGENTS.md&lt;/code&gt;、&lt;code&gt;SOUL.md&lt;/code&gt;、&lt;code&gt;TOOLS.md&lt;/code&gt;、&lt;code&gt;BOOTSTRAP.md&lt;/code&gt;、&lt;code&gt;IDENTITY.md&lt;/code&gt;、&lt;code&gt;USER.md&lt;/code&gt; 这些文件会在新 session 的第一轮直接注入上下文。技能加载也有一整套优先级：workspace 里的、项目里的、个人目录里的、&lt;code&gt;~/.openclaw/skills&lt;/code&gt; 里的、bundled 的，都能往里叠。它本质上是在搭一个“人格 + 规则 + 工作区 + 通道”统一的长期助手环境。&lt;/p&gt;
&lt;p&gt;Hermes 的技能系统明显更偏“程序化记忆”。官方文档直接说，skills 是 on-demand knowledge documents，走 progressive disclosure，兼容 &lt;code&gt;agentskills.io&lt;/code&gt; 的开放标准，统一放在 &lt;code&gt;~/.hermes/skills/&lt;/code&gt; 下面，而且 agent 自己就能修改和删除 skill。这个味道就不是“给助手配几份说明书”了，而是“把做过的事沉淀成可以再次加载的能力块”。&lt;/p&gt;
&lt;p&gt;记忆也一样。Hermes 内置的是很克制的 &lt;code&gt;MEMORY.md&lt;/code&gt; 和 &lt;code&gt;USER.md&lt;/code&gt;，再配一个 SQLite + FTS5 的 session search，另外再往外挂 8 类 memory provider。这个设计挺有意思，它没有一上来把“长期人格环境”写得特别厚，而是把短记忆、检索式回忆、外挂式长期记忆拆开了。&lt;/p&gt;
&lt;p&gt;OpenClaw 不是没有记忆，也不是没有 sub-agent、多 agent、delegate 这些进阶能力。相反，它这些都不少。但从文档结构和默认心智模型看，它更像在经营一个持续在线的 assistant workspace；Hermes 更像在经营一个会持续进化的 agent capability graph。&lt;/p&gt;
&lt;h2 id=&#34;生态为什么会越走越开&#34;&gt;生态为什么会越走越开
&lt;/h2&gt;&lt;p&gt;生态这块，表面看都是“开源 + 可扩展”，实际上也分叉得很明显。&lt;/p&gt;
&lt;p&gt;OpenClaw 的生态更宽，更偏外部接入层。官方插件可以扩 channels、model providers、tools、skills、speech、web fetch、web search、memory，社区插件和 ClawHub 也在围着“接更多平台、接更多账号、接更多工作流”长。它甚至明确兼容 &lt;code&gt;.codex-plugin&lt;/code&gt;、&lt;code&gt;.claude-plugin&lt;/code&gt;、&lt;code&gt;.cursor-plugin&lt;/code&gt; 这类 bundle 形态。这个动作很聪明，说白了就是先把别家生态的壳也吃进来，先把入口做大。&lt;/p&gt;
&lt;p&gt;再看 OpenClaw 的文档，你会发现它对 sub-agent、delegate、组织代理、thread binding、DM pairing、group mention 这些东西写得非常细。说明它的生态目标，不只是单机玩家折腾，而是希望真的变成一个长期运行的 agent operating system。&lt;/p&gt;
&lt;p&gt;Hermes 的生态则更像“内核能力外接”。它有 Python plugin system，有 memory provider plugin，有 context engine plugin，有 MCP server 接入，也有 Skills Hub 和 &lt;code&gt;agentskills.io&lt;/code&gt; 这种开放 skill 标准。再加上 batch runner、trajectory export、Atropos 这些训练相关接口，Hermes 的生态天然会吸引两类人。&lt;/p&gt;
&lt;p&gt;一类是把它当个人 agent 用的人。&lt;/p&gt;
&lt;p&gt;另一类是把它当研究、训练、实验、workflow substrate 用的人。&lt;/p&gt;
&lt;p&gt;这两类用户的需求差异很大，但 Hermes 的架构刚好都能兜住。所以你会感觉 Hermes 的生态虽然未必像 OpenClaw 那样先在“渠道数量”和“消息存在感”上铺开，但我会更倾向于觉得，它在 agent substrate 这块的后劲会更大。&lt;/p&gt;
&lt;p&gt;还有一个挺有意思的细节。Hermes 官方 README 里不光有 &lt;code&gt;hermes claw migrate&lt;/code&gt;，还挂了一个社区项目 &lt;code&gt;HermesClaw&lt;/code&gt;，用来在同一个 WeChat 账号上同时跑 Hermes Agent 和 OpenClaw。这个信号已经很明显了：Hermes 不是假装自己和 OpenClaw 没关系，它是在承认这条用户迁移链路存在，然后顺手把这条链路产品化了。&lt;/p&gt;
&lt;h2 id=&#34;我最后的判断&#34;&gt;我最后的判断
&lt;/h2&gt;&lt;p&gt;如果你要的是一个“真的活在消息面上的个人助理系统”，要的是账号、渠道、路由、delegate、组织边界、长期在线、强控制面，那 OpenClaw 这条路更完整，也更像一个成熟的通信底座。&lt;/p&gt;
&lt;p&gt;如果你要的是一个“会越用越像自己工作台的 agent core”，看重的是 skill 沉淀、memory 组合、MCP、插件、sub-agent、训练数据和后续可塑性，那 Hermes 更顺手。&lt;/p&gt;
&lt;p&gt;所以别再简单问“Hermes 能不能替代 OpenClaw”了。这个问题问得有点糙。&lt;/p&gt;
&lt;p&gt;更准确的问法应该是：&lt;/p&gt;
&lt;p&gt;你到底想要一个长在消息网络里的 AI 助理，还是一个长在本地工作流和 agent runtime 里的 AI 平台？&lt;/p&gt;
&lt;p&gt;想清楚这个，选择就没那么拧巴了。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/NousResearch/hermes-agent&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent GitHub README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.org/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent 官网 Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/developer-guide/architecture&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/skills&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Skills System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/memory&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Persistent Memory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/mcp&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent MCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://hermes-agent.nousresearch.com/docs/user-guide/features/plugins&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hermes Agent Plugins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/openclaw/openclaw&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw GitHub README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/concepts/architecture&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Gateway Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/concepts/agent&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Agent Runtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/concepts/multi-agent&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Multi-Agent Routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/concepts/delegate-architecture&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Delegate Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/tools/subagents&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Sub-Agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/tools/plugin&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Plugins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.openclaw.ai/tools/skills-config&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw Skills Config&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;提示词：Hermes 和 OpenClaw 的不同，架构上的不同，设计理念上面的不同，生态的不同&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;先把“替代品对比”这个常见问法拆掉，直接把主线定成“两个项目的中心到底放在哪”。&lt;/li&gt;
&lt;li&gt;架构部分优先看官方文档里的主入口和核心组件，而不是社区二手总结。&lt;/li&gt;
&lt;li&gt;设计理念部分不做空泛价值判断，直接落到 Gateway、Agent Core、memory、skill、delegate 这些具体设计上。&lt;/li&gt;
&lt;li&gt;生态部分重点比较扩展方向，不做星标、热度和社区口水战。&lt;/li&gt;
&lt;li&gt;事实核验以 2026-04-15 可见的官方仓库与官方文档为准。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>让 AI 写 Demo，检查点要比代码更小</title>
        <link>https://ttf248.life/p/ai-demo-fast-rework-faster/</link>
        <pubDate>Fri, 10 Apr 2026 15:39:51 +0800</pubDate>
        
        <guid>https://ttf248.life/p/ai-demo-fast-rework-faster/</guid>
        <description>&lt;p&gt;AI 写 C++ 小项目，最吓人的地方不是它写不出来，而是它太快写出来了。&lt;/p&gt;
&lt;p&gt;你只是想起一个最小 Demo，它三分钟给你铺好目录，接好三方库，补上日志、配置、异常和几个看起来很专业的类。项目甚至能跑。问题是，等你开始认真接业务，才发现自己还没搞清楚新库支持什么、构建链路怎么走、哪些抽象是必要的，返工已经排着队来了。&lt;/p&gt;
&lt;p&gt;我现在对 AI 编程的态度反而更保守：不是少用 AI，而是每一轮都把检查点压小。尤其 C++ 这种没有统一脚手架兜底的语言，前面让 AI 多做一点，后面就可能多还几步。&lt;/p&gt;
&lt;h2 id=&#34;快不是问题失去节奏才是问题&#34;&gt;快不是问题，失去节奏才是问题
&lt;/h2&gt;&lt;p&gt;人工写一个 C++ 小项目，常见节奏其实很土：&lt;/p&gt;
&lt;p&gt;先写 &lt;code&gt;main.cpp&lt;/code&gt;，让它能编。再加一小段业务逻辑，让它能跑。接着才考虑日志、配置、目录拆分和测试。每一步都不优雅，但每一步都知道自己换来了什么。&lt;/p&gt;
&lt;p&gt;AI 容易破坏这个节奏。&lt;/p&gt;
&lt;p&gt;它看到“写一个日志扫描工具”，可能顺手就给你：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/&lt;/code&gt;、&lt;code&gt;include/&lt;/code&gt;、&lt;code&gt;tests/&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fmt&lt;/code&gt;、&lt;code&gt;spdlog&lt;/code&gt;、&lt;code&gt;toml++&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;配置类、日志类、扫描类；&lt;/li&gt;
&lt;li&gt;错误处理、帮助信息、输出格式；&lt;/li&gt;
&lt;li&gt;甚至还会补一个 README。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些东西单看都不坏。真正的问题是，你的认知没有跟上项目展开速度。一个三方库引进来以后，安装方式、编译目标、异常模型、线程安全、平台差异，都可能变成后面的约束。你不理解这些约束，Demo 越完整，返工范围越大。&lt;/p&gt;
&lt;p&gt;所以 AI 编程里最该练的能力，不是让模型一次生成更多，而是让它每次只跨过一个你能验收的检查点。&lt;/p&gt;
&lt;h2 id=&#34;一个够用的小练习&#34;&gt;一个够用的小练习
&lt;/h2&gt;&lt;p&gt;我觉得可以拿一个很小的 C++ 工具练手：&lt;code&gt;logscan&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;它只做一件事：扫描一个目录里的日志文件，统计 &lt;code&gt;error&lt;/code&gt; 和 &lt;code&gt;warn&lt;/code&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;li&gt;简单业务模块。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关键是，不要让 AI 一口气做完。把它切成四轮。&lt;/p&gt;
&lt;h2 id=&#34;第一轮只接-fmt&#34;&gt;第一轮只接 fmt
&lt;/h2&gt;&lt;p&gt;第一轮只做输出。&lt;/p&gt;
&lt;p&gt;目标很小：根目录有一个 &lt;code&gt;CMakeLists.txt&lt;/code&gt;，有一个 &lt;code&gt;main.cpp&lt;/code&gt;，能用 &lt;code&gt;fmt&lt;/code&gt; 打印扫描目录和统计结果。业务扫描可以先是假数据。&lt;/p&gt;
&lt;p&gt;这一步的 prompt 可以写得很死：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;当前只完成一个检查点：项目能编译，main.cpp 能用 fmt 打印目录和统计结果。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;不要新增日志模块、配置模块、测试框架或复杂目录结构。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;先说明修改哪些文件，再给代码。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;完成后给出构建命令和预期输出。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里不是不让 AI 发挥，而是先确认一件事：依赖怎么接进来，项目怎么编译，输出是否符合预期。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fmt&lt;/code&gt; 有清楚的 CMake 用法，CMake 的 &lt;code&gt;FetchContent&lt;/code&gt; 也适合小项目起步。你可以让 AI 按其中一种方式做，但不要让它一轮里同时尝试 &lt;code&gt;find_package&lt;/code&gt;、手拷源码、&lt;code&gt;add_subdirectory&lt;/code&gt; 和包管理器。&lt;/p&gt;
&lt;p&gt;第一轮的目标不是“架构好看”，而是把构建链路钉住。&lt;/p&gt;
&lt;h2 id=&#34;第二轮再加日志&#34;&gt;第二轮再加日志
&lt;/h2&gt;&lt;p&gt;等第一轮能稳定编译，再上 &lt;code&gt;spdlog&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这一步也要压住范围。不要一开始就让 AI 做滚动日志、按天切分、全局 logger 工厂和配置驱动。先只做控制台日志，最多再加一个简单文件日志。&lt;/p&gt;
&lt;p&gt;这一轮真正要确认的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;日志初始化放在哪里；&lt;/li&gt;
&lt;li&gt;业务逻辑怎么拿 logger；&lt;/li&gt;
&lt;li&gt;正常输出和调试日志怎么分工；&lt;/li&gt;
&lt;li&gt;失败时日志里能看到什么。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;spdlog&lt;/code&gt; 能做很多事，但练习的重点不是把它所有能力都接上。重点是确认你的项目真的需要哪些能力。&lt;/p&gt;
&lt;p&gt;AI 很容易把“库支持”误写成“项目需要”。这一步要拦住它。&lt;/p&gt;
&lt;h2 id=&#34;第三轮才读配置&#34;&gt;第三轮才读配置
&lt;/h2&gt;&lt;p&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;/ul&gt;
&lt;p&gt;AI 可能会顺手写成配置中心、热更新、环境变量覆盖、默认值合并、校验层、错误码体系。不是这些东西没用，而是你还没证明现在需要它们。&lt;/p&gt;
&lt;p&gt;如果用 &lt;code&gt;toml++&lt;/code&gt;，这一轮只读一个 &lt;code&gt;config.toml&lt;/code&gt;，解析成一个很薄的 &lt;code&gt;AppConfig&lt;/code&gt; 结构体就够了。&lt;/p&gt;
&lt;p&gt;不要让配置层先于业务长大。配置只是服务当前工具，不是给未来所有想象中的版本做平台。&lt;/p&gt;
&lt;h2 id=&#34;第四轮再拆模块&#34;&gt;第四轮再拆模块
&lt;/h2&gt;&lt;p&gt;等输出、日志、配置都跑过，再拆业务模块。&lt;/p&gt;
&lt;p&gt;这时候可以收成几个文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;main.cpp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;app_config.h / app_config.cpp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;logger.h / logger.cpp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;log_scanner.h / log_scanner.cpp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意顺序：是先跑通，再拆。&lt;/p&gt;
&lt;p&gt;第一步就把目录排得很漂亮，不一定让项目更清楚。很多时候只是视觉上完整，问题来源反而更难追。等每个依赖和职责都实际跑过一轮，再让 AI 帮你整理，返工成本会低很多。&lt;/p&gt;
&lt;h2 id=&#34;我现在只留三条规则&#34;&gt;我现在只留三条规则
&lt;/h2&gt;&lt;p&gt;网上 AI 编程规范很多，复杂到最后很难执行。我自己现在只保留三条。&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;/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;不要在同一轮里同时“顺手重构”“顺手补配置”“顺手引日志”。C++ 里最怕顺手，因为顺手会把问题来源弄脏。&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;/ul&gt;
&lt;p&gt;这三条看起来不像什么新方法，甚至有点笨。但笨办法抗返工。AI 已经足够快了，人要补上的不是更快，而是把它的快切成能验收的小段。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.anthropic.com/en/docs/claude-code/common-workflows&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Anthropic, Claude Code Common Workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.anthropic.com/en/docs/claude-code/overview&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Anthropic, Claude Code Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://docs.github.com/en/copilot/tutorials/cloud-agent/get-the-best-results&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GitHub Docs, Best practices for using GitHub Copilot to work on tasks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://cmake.org/cmake/help/latest/module/FetchContent.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;CMake Docs, FetchContent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://fmt.dev/latest/get-started/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;{fmt} Get Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/gabime/spdlog&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gabime/spdlog README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/gabime/spdlog/wiki/Sinks&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gabime/spdlog Wiki: Sinks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/marzer/tomlplusplus&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;marzer/tomlplusplus README&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;提示词：人工写项目，针对 C++ 这种不用手脚架的语言，第一个弄个 框架，从 main 开始拓展逻辑，逐步增加模块，比如配置文件、日志模块、迭代业务模块。我的习惯是每次都能确保编译，逐步重构，可能一开始不是最优解。AI 编程，现在很多时候，跳过了步骤，前期思考的太少，特别是引入新的三方库，AI 是能快速出个 Demo，但是由于我不清楚新引入三方库的细节，支持哪些功能，相关逻辑的返工率明显高于人工开发。有没有好的实战案例，让我学下，我知道网上有很多复杂的 AI 编程规范，我想要简单一些的，能快速落地的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;保留原始提示词里的 C++ 小项目、逐步编译、逐步重构和三方库返工问题。&lt;/li&gt;
&lt;li&gt;把旧稿的步骤教程压成一个练习路径，重点放在“每轮只过一个检查点”。&lt;/li&gt;
&lt;li&gt;删除泛泛的 AI 编程规范感，保留能立刻执行的三条规则。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>VS Code 调 C&#43;&#43;，CMake 和 GDB Printer 要一起配</title>
        <link>https://ttf248.life/p/vscode-cpp-debug-prelaunch-cmake-gdb-printer/</link>
        <pubDate>Thu, 09 Apr 2026 19:21:36 +0800</pubDate>
        
        <guid>https://ttf248.life/p/vscode-cpp-debug-prelaunch-cmake-gdb-printer/</guid>
        <description>&lt;p&gt;以前我在 VS Code 里调 C++，配置基本就停在 &lt;code&gt;launch.json&lt;/code&gt;，最多再加一句 GDB。&lt;/p&gt;
&lt;p&gt;把 &lt;code&gt;program&lt;/code&gt; 填好，把 &lt;code&gt;gdb&lt;/code&gt; 填好，把断点打好。然后呢？然后每次调试前，自己去终端里 &lt;code&gt;cmake --build&lt;/code&gt; 一下。&lt;/p&gt;
&lt;p&gt;更烦的是，自定义的价格、合约、订单类型断下来以后，VS Code 调试窗口经常只能看到一堆内部字段。数据是对的，但是不说人话。&lt;/p&gt;
&lt;p&gt;这事离谱的地方在于，我以前看过的一些教程也差不多就写到 &lt;code&gt;launch.json&lt;/code&gt; 为止。直到最近让 AI 配一个新项目，它顺手加了 &lt;code&gt;preLaunchTask&lt;/code&gt; 和 &lt;code&gt;gdb_printers.py&lt;/code&gt;，我才反应过来：VS Code 调 C++，不只是把 GDB 启起来。调试前可以自动触发 CMake 编译，断点停住以后，也可以让 GDB 加载 Python 脚本，把业务类型处理成自己看得懂的样子。&lt;/p&gt;
&lt;p&gt;说实话，这不是什么黑科技。&lt;/p&gt;
&lt;p&gt;但是它刚好补上了 C++ 日常调试里两块很烦的空白：&lt;strong&gt;启动前构建&lt;/strong&gt;，以及&lt;strong&gt;断点后的变量展示&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;以前只配了一半&#34;&gt;以前只配了一半
&lt;/h2&gt;&lt;p&gt;很多教程里的 &lt;code&gt;.vscode/launch.json&lt;/code&gt;，大概是这个意思：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0.2.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;configurations&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Debug with GDB&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cppdbg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;request&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;program&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${workspaceFolder}/build/debug/bin/vscode_cpp_debug_demo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;stopAtEntry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;cwd&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${workspaceFolder}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;MIMode&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;gdb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;miDebuggerPath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/gdb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;setupCommands&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Enable pretty-printing for gdb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-enable-pretty-printing&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignoreFailures&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&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;p&gt;VS Code 官方的 C++ 示例里，也是类似这一套结构：&lt;code&gt;type&lt;/code&gt; 用 &lt;code&gt;cppdbg&lt;/code&gt;，&lt;code&gt;MIMode&lt;/code&gt; 用 &lt;code&gt;gdb&lt;/code&gt;，&lt;code&gt;setupCommands&lt;/code&gt; 里打开 pretty-printing。&lt;/p&gt;
&lt;p&gt;但是少了一个关键动作。&lt;/p&gt;
&lt;p&gt;你点 F5 的时候，它会去启动 &lt;code&gt;program&lt;/code&gt; 指向的那个可执行文件。问题是，这个文件是不是刚刚编出来的？&lt;/p&gt;
&lt;p&gt;如果不是，那你调的就是上一次的旧程序。&lt;/p&gt;
&lt;p&gt;我以前就经常干这种蠢事。代码改了，断点也打了，调半天发现行为不对，最后才想起来：哦，刚刚没重新编译。&lt;/p&gt;
&lt;h2 id=&#34;prelaunchtask-才是那个钩子&#34;&gt;preLaunchTask 才是那个钩子
&lt;/h2&gt;&lt;p&gt;VS Code 的 Tasks，本质上就是把外部命令挂进编辑器。&lt;/p&gt;
&lt;p&gt;编译、测试、打包，原来要在终端敲，现在可以写到 &lt;code&gt;.vscode/tasks.json&lt;/code&gt;。然后 &lt;code&gt;launch.json&lt;/code&gt; 里的 &lt;code&gt;preLaunchTask&lt;/code&gt;，再按 label 去找那一个 task。&lt;/p&gt;
&lt;p&gt;一个最小的 CMake 项目，可以这么放：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── .vscode/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── launch.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── tasks.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── CMakeLists.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── src/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── main.cpp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── tools/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── gdb_printers.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;CMakeLists.txt&lt;/code&gt; 先写到够用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cmake&#34; data-lang=&#34;cmake&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cmake_minimum_required&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;VERSION&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;3.16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;project&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;vscode_cpp_debug_demo&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;LANGUAGES&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;CXX&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;CMAKE_CXX_STANDARD&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;CMAKE_CXX_STANDARD_REQUIRED&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;CMAKE_RUNTIME_OUTPUT_DIRECTORY&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;CMAKE_BINARY_DIR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;/bin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;err&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;add_executable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;vscode_cpp_debug_demo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s&#34;&gt;src/main.cpp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;err&#34;&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;.vscode/tasks.json&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2.0.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;tasks&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cmake: configure debug&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;process&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cmake&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;-S&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${workspaceFolder}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;-B&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${workspaceFolder}/build/debug&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;-DCMAKE_BUILD_TYPE=Debug&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;problemMatcher&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cmake: build debug&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;process&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cmake&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;--build&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${workspaceFolder}/build/debug&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;--parallel&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;dependsOn&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;cmake: configure debug&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;group&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;kind&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;&amp;#34;isDefault&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;problemMatcher&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;$gcc&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&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;cmake --build&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;CMake 官方给的构建入口就是这个形式：&lt;code&gt;cmake --build &amp;lt;dir&amp;gt;&lt;/code&gt;。它会去调用背后的原生构建工具，比如 Make、Ninja、MSBuild 这一类。&lt;/p&gt;
&lt;p&gt;也就是说，VS Code 不需要知道你的项目到底该敲 &lt;code&gt;make&lt;/code&gt; 还是 &lt;code&gt;ninja&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这件事交给 CMake。&lt;/p&gt;
&lt;h2 id=&#34;f5-之前先编译&#34;&gt;F5 之前，先编译
&lt;/h2&gt;&lt;p&gt;然后回到 &lt;code&gt;.vscode/launch.json&lt;/code&gt;，把 &lt;code&gt;preLaunchTask&lt;/code&gt; 补进去：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;0.2.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;configurations&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CMake Debug with GDB&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cppdbg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;request&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;program&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${workspaceFolder}/build/debug/bin/vscode_cpp_debug_demo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;stopAtEntry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;cwd&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;${workspaceFolder}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;environment&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;externalConsole&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;MIMode&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;gdb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;miDebuggerPath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/usr/bin/gdb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;setupCommands&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Enable pretty-printing for gdb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-enable-pretty-printing&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;          &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignoreFailures&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;nt&#34;&gt;&amp;#34;preLaunchTask&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cmake: build debug&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&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=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;preLaunchTask&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cmake: build debug&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;按 F5 的链路就变成了：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;launch.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -&amp;gt; preLaunchTask
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    -&amp;gt; tasks.json: cmake: build debug
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      -&amp;gt; dependsOn: cmake: configure debug
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        -&amp;gt; cmake -S ... -B ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      -&amp;gt; cmake --build ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -&amp;gt; gdb 启动最新的 program
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果是 Windows + MinGW，&lt;code&gt;program&lt;/code&gt; 大概率要改成带 &lt;code&gt;.exe&lt;/code&gt; 的路径，&lt;code&gt;miDebuggerPath&lt;/code&gt; 也要指到自己的 &lt;code&gt;gdb.exe&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果是 Visual Studio Generator 那种多配置生成器，构建参数通常还要加 &lt;code&gt;--config Debug&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但是主线不变。&lt;/p&gt;
&lt;p&gt;调试前让 VS Code 先跑一次构建任务，别靠自己脑子记。&lt;/p&gt;
&lt;h2 id=&#34;还有一个痛点自定义类型看不懂&#34;&gt;还有一个痛点：自定义类型看不懂
&lt;/h2&gt;&lt;p&gt;只把编译链路打通，还不够。&lt;/p&gt;
&lt;p&gt;C++ 代码里稍微封装一点业务类型，VS Code 左边那个变量窗口就开始变得难看。&lt;/p&gt;
&lt;p&gt;比如下面这个例子。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/main.cpp&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;cstdint&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;cstdio&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;string_view&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;namespace&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;market&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Price&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int64_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Price&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;from_double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Price&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;static_cast&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int64_t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;to_double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;static_cast&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;10000.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Instrument&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;16&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Price&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;last&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Instrument&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;make_instrument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string_view&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Instrument&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instrument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;snprintf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instrument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instrument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;%s&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;instrument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Price&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instrument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// namespace market
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;market&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Instrument&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;watchlist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;market&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;make_instrument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;IF2406&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;3578.6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;market&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;make_instrument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;IH2406&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;2468.2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;market&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Price&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;limit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;market&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Price&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;from_double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;3600.5&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 在这里打断点，观察 watchlist 和 limit。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;watchlist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;limit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&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;market::Price&lt;/code&gt; 在业务里是价格。&lt;/p&gt;
&lt;p&gt;但是调试器默认看到的可能只有：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;limit = {raw = 36005000}
&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;code&gt;36005000&lt;/code&gt; 换算成 &lt;code&gt;3600.5000&lt;/code&gt;。如果项目里有一堆 &lt;code&gt;Price&lt;/code&gt;、&lt;code&gt;Quantity&lt;/code&gt;、&lt;code&gt;OrderId&lt;/code&gt;、&lt;code&gt;Instrument&lt;/code&gt;、&lt;code&gt;AlgoState&lt;/code&gt;，调试窗口很快就变成结构体坟场。&lt;/p&gt;
&lt;p&gt;这个时候就该上 GDB pretty-printer 了。&lt;/p&gt;
&lt;p&gt;GDB 官方手册说得很直接：它提供了一套机制，可以用 Python 代码 pretty-print 值，而且这个机制同时适用于 MI 和命令行。&lt;/p&gt;
&lt;p&gt;VS Code 的 C++ 调试刚好走的就是 GDB/MI 这条路。&lt;/p&gt;
&lt;h2 id=&#34;给业务类型写一个-python-展示层&#34;&gt;给业务类型写一个 Python 展示层
&lt;/h2&gt;&lt;p&gt;新建 &lt;code&gt;tools/gdb_printers.py&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;gdb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;gdb.printing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;PricePrinter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;to_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;raw&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;10000.0&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.4f&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;InstrumentPrinter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;fm&#34;&gt;__init__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;val&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;to_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;symbol&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;price_raw&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;val&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;last&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;raw&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;price&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;price_raw&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;10000.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;sa&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; last=&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.4f&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;build_pretty_printer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;printer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;gdb&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;printing&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;RegexpCollectionPrettyPrinter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;market&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;printer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add_printer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Price&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;^market::Price$&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PricePrinter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;printer&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add_printer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Instrument&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;^market::Instrument$&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;InstrumentPrinter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;printer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;gdb&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;printing&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;register_pretty_printer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;gdb&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_objfile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;build_pretty_printer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;replace&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&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;launch.json&lt;/code&gt; 的 &lt;code&gt;setupCommands&lt;/code&gt; 里把它 source 进去：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;setupCommands&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Enable pretty-printing for gdb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-enable-pretty-printing&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignoreFailures&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Load project pretty-printers&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-interpreter-exec console \&amp;#34;source ${workspaceFolder}/tools/gdb_printers.py\&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;ignoreFailures&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重新 F5。&lt;/p&gt;
&lt;p&gt;这时候再看调试窗口，&lt;code&gt;Price&lt;/code&gt; 的展示就应该接近：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;limit = 3600.5000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Instrument&lt;/code&gt; 也会从一坨数组、结构体，变成更接近业务的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;IF2406 last=3578.6000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里我故意说“接近”，因为 pretty-printer 要和编译器、类型名字、真实字段布局匹配。&lt;/p&gt;
&lt;p&gt;如果你的业务类型里面又套了 &lt;code&gt;std::vector&lt;/code&gt;、&lt;code&gt;std::array&lt;/code&gt;、智能指针，Python 脚本里访问的字段可能还要继续调。不要指望一个脚本通吃所有项目。&lt;/p&gt;
&lt;p&gt;但是思路是稳的。&lt;/p&gt;
&lt;p&gt;把“调试时人想看的样子”，单独写成一层 Python 展示逻辑。&lt;/p&gt;
&lt;h2 id=&#34;别把-launchjson-当成全部&#34;&gt;别把 launch.json 当成全部
&lt;/h2&gt;&lt;p&gt;现在再看这套配置，我觉得至少有三层。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.vscode/launch.json&lt;/code&gt; 负责“怎么启动调试器”。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.vscode/tasks.json&lt;/code&gt; 负责“启动调试器之前，先把项目编出来”。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tools/gdb_printers.py&lt;/code&gt; 负责“断住以后，变量用人能看懂的方式展示”。&lt;/p&gt;
&lt;p&gt;以前我只配第一层。&lt;/p&gt;
&lt;p&gt;能用，但是很毛坯。&lt;/p&gt;
&lt;p&gt;真正顺手的 C++ 调试，不应该是“我先去终端编译一下，再回来按 F5，再在变量窗口里心算字段含义”。&lt;/p&gt;
&lt;p&gt;更舒服的链路应该是：按 F5，VS Code 自动跑 CMake，GDB 自动加载项目的 Python printer，断点停下来以后，调试窗口直接说业务语言。&lt;/p&gt;
&lt;p&gt;AI 这次帮我配项目，最有价值的地方倒不是生成了多少 C++ 代码。&lt;/p&gt;
&lt;p&gt;而是它把这些老工具之间的胶水也补上了。&lt;/p&gt;
&lt;p&gt;我以前不是不知道 CMake，也不是不知道 GDB。&lt;/p&gt;
&lt;p&gt;只是少配了中间那几个钩子。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://code.visualstudio.com/docs/cpp/config-linux&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Using C++ on Linux in VS Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://code.visualstudio.com/docs/debugtest/tasks&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;VS Code: Integrate with External Tools via Tasks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://cmake.org/cmake/help/latest/manual/cmake.1.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;CMake cmake(1) manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GDB Manual: Pretty Printing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-a-Pretty_002dPrinter.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GDB Manual: Writing a Pretty-Printer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;提示词：$blog-writer vscode 进行 C++ 开发，以前查看的教程，都只是简单的配置了 launch.json 里面的 gdb 调试信息，都没提到配置 preLaunchTask，能做到自动触发 cmake 编译，这还是 AI 配置新项目发现的好东西；如果代码有很多自定义的数据类型，vscode 调试窗口无法直接展示，可以引入 python 脚本进行预处理，这样都能展示了。针对上面的内容，都需要提供实际的代码案例。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;主线放在“以前只配 launch.json，其实少了调试前构建和调试时展示两层胶水”。&lt;/li&gt;
&lt;li&gt;保留用户给出的触发点：AI 配新项目时发现 &lt;code&gt;preLaunchTask&lt;/code&gt; 可以自动触发 CMake 编译。&lt;/li&gt;
&lt;li&gt;代码案例按一个最小 CMake 项目组织，包含 &lt;code&gt;CMakeLists.txt&lt;/code&gt;、&lt;code&gt;tasks.json&lt;/code&gt;、&lt;code&gt;launch.json&lt;/code&gt;、C++ 自定义类型和 GDB Python pretty-printer。&lt;/li&gt;
&lt;li&gt;事实核验优先看 VS Code、CMake、GDB 官方文档；正文不翻译文档，只保留和这条调试链路有关的事实。&lt;/li&gt;
&lt;li&gt;对 pretty-printer 的边界做了提醒：类型名、标准库实现、字段布局会影响 Python 脚本，项目里要按自己的类型改。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>文件夹分层再套 namespace，这事到底叫什么</title>
        <link>https://ttf248.life/p/cpp-folder-namespace-what-is-it-called/</link>
        <pubDate>Thu, 09 Apr 2026 02:31:07 +0800</pubDate>
        
        <guid>https://ttf248.life/p/cpp-folder-namespace-what-is-it-called/</guid>
        <description>&lt;p&gt;最近写算法服务，&lt;code&gt;twap&lt;/code&gt;、&lt;code&gt;vwap&lt;/code&gt; 这类模块一铺开，我又把这个老问题翻出来了。&lt;/p&gt;
&lt;p&gt;C++ 里如果还靠类名硬扛语义，名字很快就会失控。&lt;code&gt;TwapOrderManager&lt;/code&gt;、&lt;code&gt;VwapOrderManager&lt;/code&gt;、&lt;code&gt;AlgoOrderManager&lt;/code&gt; 这种东西，写着写着就一股子“我知道自己结构没收住，但我先把前缀补上”的味道。说白了，按文件夹分层，再配一层 &lt;code&gt;namespace&lt;/code&gt;，不是代码洁癖，这是在补 C++ 没有 Java 那种原生 &lt;code&gt;package&lt;/code&gt; 体系的空位。&lt;/p&gt;
&lt;h2 id=&#34;先说结论&#34;&gt;先说结论
&lt;/h2&gt;&lt;p&gt;如果非要找一个最贴近的软件工程名词，我觉得最该说的是两个词：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;模块化&lt;/strong&gt;（modularization）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命名空间管理&lt;/strong&gt;（namespace management）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果再说得更贴一点，这类目录组织往往是在做：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;按特性分包 / 按领域分包&lt;/strong&gt;，也就是常说的 &lt;code&gt;package by feature&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;如果业务边界已经很强，还会带一点 &lt;strong&gt;bounded context&lt;/strong&gt; 的味道&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，它不是一个只有单一标准译名的小概念，更像是几套设计原则叠在一起。&lt;/p&gt;
&lt;h2 id=&#34;这事先解决的其实不是好看&#34;&gt;这事先解决的，其实不是“好看”
&lt;/h2&gt;&lt;p&gt;很多人一看到 &lt;code&gt;namespace&lt;/code&gt;，第一反应是避免重名。这个当然对，C++ 标准里的 &lt;code&gt;namespace&lt;/code&gt; 本来就是拿来防止大型项目里名字冲突的 &lt;a class=&#34;link&#34; href=&#34;https://en.cppreference.com/w/cpp/language/namespace&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;cppreference&lt;/a&gt;。但真到业务代码里，它的价值远不止这个。&lt;/p&gt;
&lt;p&gt;目录和 &lt;code&gt;namespace&lt;/code&gt; 一旦对齐，类名就不用再把上层语义重复一遍了。&lt;/p&gt;
&lt;p&gt;比如以前可能会写成这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;TwapOrderManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;TwapScheduleEngine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;VwapOrderManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;VwapScheduleEngine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&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=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;namespace&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;algo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;twap&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;OrderManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ScheduleEngine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;namespace&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;algo&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;vwap&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;OrderManager&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;ScheduleEngine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&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;namespace&lt;/code&gt; 来表达。&lt;/p&gt;
&lt;p&gt;所以这套做法，本质上是在把&lt;strong&gt;上下文信息从名字里抽出去，交给结构来承载&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;这背后更像按特性分包&#34;&gt;这背后更像“按特性分包”
&lt;/h2&gt;&lt;p&gt;如果你的目录是这样长的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;strategy/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  twap/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    order_manager.h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    schedule_engine.h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    slicer.h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  vwap/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    order_manager.h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    schedule_engine.h
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    slicer.h
&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;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;models/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;services/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;utils/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;controllers/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种分法前期看着整齐，后面业务一长，&lt;code&gt;service&lt;/code&gt; 目录就会像垃圾回收站。跟一个功能相关的类会被打散到不同目录里，读代码的时候脑子一直在跳。&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;twap&lt;/code&gt;、&lt;code&gt;vwap&lt;/code&gt; 这种分法，更接近 &lt;code&gt;package by feature&lt;/code&gt;。也就是一个目录先回答“这块业务是干什么的”，再在里面放这块业务自己需要的对象和流程。Java 世界里这个说法更常见，但放到 C++ 一样成立，只是承载它的不是原生 &lt;code&gt;package&lt;/code&gt;，而是“目录 + &lt;code&gt;namespace&lt;/code&gt; + 头文件边界”。&lt;/p&gt;
&lt;p&gt;说白了，你是在&lt;strong&gt;按变化原因组织代码，而不是按代码长相组织代码&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;再往上走一步就是高内聚低耦合&#34;&gt;再往上走一步，就是高内聚低耦合
&lt;/h2&gt;&lt;p&gt;软件工程里老生常谈的那句“高内聚、低耦合”，其实就落在这种地方。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;twap&lt;/code&gt; 下面的对象彼此协作频繁，就应该放得近一点，命名空间也靠得近一点；&lt;code&gt;vwap&lt;/code&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;这也是为什么很多成熟项目最后都会长成“目录边界 + 命名空间边界 + 编译边界”尽量一致的样子。QuickFIX 这类项目里，你也能看到类似思路，类型和扩展点都尽量放在清晰的命名空间下面，而不是靠巨长的类名前缀去硬区分 &lt;a class=&#34;link&#34; href=&#34;https://quickfixengine.org/c/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;QuickFIX&lt;/a&gt; &lt;a class=&#34;link&#34; href=&#34;https://github.com/quickfix/quickfix&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GitHub&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;什么时候它又不够了&#34;&gt;什么时候它又不够了
&lt;/h2&gt;&lt;p&gt;不过这事也别神化。&lt;/p&gt;
&lt;p&gt;文件夹分层再套 &lt;code&gt;namespace&lt;/code&gt;，只能说明你在往模块化方向走，不代表架构就自动好了。几个常见坑也很明显：&lt;/p&gt;
&lt;h3 id=&#34;目录和命名空间对不上&#34;&gt;目录和命名空间对不上
&lt;/h3&gt;&lt;p&gt;目录是 &lt;code&gt;twap/&lt;/code&gt;，代码里却还是一堆散着的全局类，或者 &lt;code&gt;namespace common&lt;/code&gt; 到处乱飞，这种基本等于白分。&lt;/p&gt;
&lt;h3 id=&#34;模块边界是假边界&#34;&gt;模块边界是假边界
&lt;/h3&gt;&lt;p&gt;表面上有 &lt;code&gt;twap&lt;/code&gt;、&lt;code&gt;vwap&lt;/code&gt;，实际上互相 &lt;code&gt;#include&lt;/code&gt;，公共逻辑随便穿透，最后只是把文件挪了位置，耦合一点没降。&lt;/p&gt;
&lt;h3 id=&#34;common-目录越长越胖&#34;&gt;&lt;code&gt;common&lt;/code&gt; 目录越长越胖
&lt;/h3&gt;&lt;p&gt;这个最常见。很多项目前面切得挺好，后面嫌麻烦，开始往 &lt;code&gt;common&lt;/code&gt;、&lt;code&gt;base&lt;/code&gt;、&lt;code&gt;util&lt;/code&gt; 里丢东西。丢到最后，真正稳定的抽象没沉淀下来，反而多了一个谁都能动、谁都依赖的大坑。&lt;/p&gt;
&lt;h3 id=&#34;结构只按层不按业务&#34;&gt;结构只按层，不按业务
&lt;/h3&gt;&lt;p&gt;如果你的目录永远是 &lt;code&gt;api&lt;/code&gt;、&lt;code&gt;service&lt;/code&gt;、&lt;code&gt;dao&lt;/code&gt;、&lt;code&gt;model&lt;/code&gt;，那很多业务概念其实没有家。类名只好越来越长，把本该由结构表达的东西全塞回名字里。&lt;/p&gt;
&lt;h2 id=&#34;那它到底该叫啥&#34;&gt;那它到底该叫啥
&lt;/h2&gt;&lt;p&gt;如果是在团队里沟通，我觉得可以分三档说法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只想说人话：&lt;strong&gt;按目录和命名空间做模块化&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;想说得更工程一点：&lt;strong&gt;按特性分包的组织方式，配合层级命名空间&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;如果这套边界已经和业务语义强绑定了：&lt;strong&gt;带 bounded context 味道的模块划分&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我自己更倾向第二种。因为它最贴近你描述的场景。&lt;/p&gt;
&lt;p&gt;你说的不是 C++ 语法技巧，也不是单纯命名规范，而是在做一件更实在的事：&lt;strong&gt;用结构承载语义，让名字回归本义&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;类名短下来，文件名短下来，读代码时先看到的是 &lt;code&gt;twap::OrderManager&lt;/code&gt; 这种上下文明确的对象，而不是 &lt;code&gt;TwapOrderManagerImpl&lt;/code&gt; 这种把目录职责、命名职责、实现职责全搅在一起的东西。&lt;/p&gt;
&lt;p&gt;这时候代码才有点像 Java 里包结构成熟之后那种感觉。&lt;/p&gt;
&lt;h2 id=&#34;最后一句&#34;&gt;最后一句
&lt;/h2&gt;&lt;p&gt;所以，软件工程上当然有对应概念，但不是只有一个标准答案。&lt;/p&gt;
&lt;p&gt;往大了说，它叫模块化。往代码组织上说，它叫按特性分包，再加命名空间管理。往领域边界上说，它又有点 bounded context 的意思。&lt;/p&gt;
&lt;p&gt;怎么说呢，这种实践最值钱的地方，从来不是“名字看起来整齐了”，而是你终于不用靠超长前缀去弥补结构缺位了。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.cppreference.com/w/cpp/language/namespace&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;cppreference: Namespaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://quickfixengine.org/c/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;QuickFIX/C++ 官方站点&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/quickfix/quickfix&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;QuickFIX GitHub 仓库&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.javapractices.com/home/HomeAction.do&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Java Practices: Package by feature, not layer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://martinfowler.com/bliki/BoundedContext.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Martin Fowler: Bounded Context&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;提示词：在编码开发的时候，C++一种好的实践规范，那就是按照文件夹分层，然后每个类上面套个 namespace，类似 Java 的 package，能有效减少文件名称和类名字里面掺杂冗余的内容,有个经典的项目就是 quickfix，近期开发算法服务也用到了类似的设计，twap vwap 很多功能模块都是类似的，软件工程学上有没有专门的概念？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;从算法服务里 &lt;code&gt;twap&lt;/code&gt;、&lt;code&gt;vwap&lt;/code&gt; 的真实模块触发点切入，先把判断亮出来。&lt;/li&gt;
&lt;li&gt;没把问题硬解释成单一名词，而是拆成模块化、命名空间管理、按特性分包几个更准确的层次。&lt;/li&gt;
&lt;li&gt;保留了 QuickFIX 和 Java package 的对照关系，用来说明 C++ 经常要靠目录和命名空间补结构语义。&lt;/li&gt;
&lt;li&gt;重点落在“用结构承载语义”，而不是停留在“避免重名”这种表层解释。&lt;/li&gt;
&lt;li&gt;补了 &lt;code&gt;namespace&lt;/code&gt;、&lt;code&gt;package by feature&lt;/code&gt;、&lt;code&gt;bounded context&lt;/code&gt; 相关参考链接，方便后续继续展开。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Gemma 4 开放以后（三）：显存速度决定本地体验</title>
        <link>https://ttf248.life/p/gemma-4-series-vram-cliff-and-mac-unified-memory/</link>
        <pubDate>Wed, 08 Apr 2026 23:56:20 +0800</pubDate>
        
        <guid>https://ttf248.life/p/gemma-4-series-vram-cliff-and-mac-unified-memory/</guid>
        <description>&lt;p&gt;这次刷论坛，最让我长记性的不是哪家又发了榜单，而是一句很土的话，显存不够，参数再大也白搭。&lt;/p&gt;
&lt;p&gt;以前我总把“模型慢”理解成算力问题。后来越看越明白，很多时候根本不是 GPU 算不动，而是数据没法待在对的地方。只要内存路径一变，token 速度就不是慢一点，是直接掉下去。&lt;/p&gt;
&lt;p&gt;前两篇已经把前置问题收完了。&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/gemma-4-series-models-and-license/&#34; &gt;第一篇&lt;/a&gt; 讲发布和协议，&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/gemma-4-series-local-test-on-rtx-3060/&#34; &gt;第二篇&lt;/a&gt; 讲 3060 12GB 上为什么先看 &lt;code&gt;26B A4B&lt;/code&gt;。最后这一篇，就只讲速度到底是怎么塌的。&lt;/p&gt;
&lt;h2 id=&#34;显存不够慢的不是一点点&#34;&gt;显存不够，慢的不是一点点
&lt;/h2&gt;&lt;p&gt;推理这件事，拆开看就两块特别关键：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模型权重&lt;/li&gt;
&lt;li&gt;KV cache&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;权重决定模型本身，KV cache 记录前文状态。上下文越长，KV cache 越大。只要这两块都能稳稳留在 GPU 显存里，生成 token 的时候基本就是在高带宽显存里取数据、做计算、回写结果，速度通常还能看。&lt;/p&gt;
&lt;p&gt;真正麻烦的，是显存装不下。&lt;/p&gt;
&lt;p&gt;一旦装不下，推理框架就得退让：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一部分权重放系统内存&lt;/li&gt;
&lt;li&gt;或者一部分 KV cache 放系统内存&lt;/li&gt;
&lt;li&gt;或者干脆 CPU 和 GPU 来回搬&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这时候问题就不是“多算了点”，而是“每出一个 token 都要等数据”。&lt;/p&gt;
&lt;h2 id=&#34;为什么会出现断崖不是线性变慢&#34;&gt;为什么会出现断崖，不是线性变慢
&lt;/h2&gt;&lt;p&gt;很多人第一次遇到这个坑，直觉都会觉得，模型从 &lt;code&gt;14B&lt;/code&gt; 变成 &lt;code&gt;31B&lt;/code&gt;，那就慢一倍多，忍一忍也行。&lt;/p&gt;
&lt;p&gt;现实不是这样。&lt;/p&gt;
&lt;p&gt;真正的分界线不是参数翻倍，而是工作集有没有跨过显存边界。&lt;/p&gt;
&lt;p&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;/ul&gt;
&lt;p&gt;路径一换，代价就完全不一样了。尤其是 decode 阶段，本来就是逐 token 往前走，batch 也不大，这时候最怕的就是每个 token 都要跨设备去等一批数据回来。&lt;/p&gt;
&lt;p&gt;所以你会看到一种很典型的现象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模型不是不能跑&lt;/li&gt;
&lt;li&gt;GPU 也不是完全闲着&lt;/li&gt;
&lt;li&gt;但 token/s 直接很难看&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是大家说的“断崖”。不是模型突然变蠢，是内存路径突然变笨了。&lt;/p&gt;
&lt;h2 id=&#34;26b-a4b-为什么对本地玩家友好一点&#34;&gt;&lt;code&gt;26B A4B&lt;/code&gt; 为什么对本地玩家友好一点
&lt;/h2&gt;&lt;p&gt;我前一篇一直更看重 &lt;code&gt;26B A4B&lt;/code&gt;，原因也在这里。&lt;/p&gt;
&lt;p&gt;它的总盘子当然不小，但每个 token 真正激活的大约只有 &lt;code&gt;3.8B&lt;/code&gt;。这意味着在相似部署条件下，它对计算和显存路径的压力，往往比 dense 大模型更容易控制。&lt;/p&gt;
&lt;p&gt;这也不是魔法。&lt;/p&gt;
&lt;p&gt;如果你上下文拉太长，或者量化和框架支持不到位，它一样会难受。只是相比那种一上来就把整块显存顶满的 dense 模型，&lt;code&gt;26B A4B&lt;/code&gt; 更像是留给消费级显卡的一条现实路线。&lt;/p&gt;
&lt;p&gt;所以很多时候不是 &lt;code&gt;31B&lt;/code&gt; 不强，而是本地机器更适合和谁长期相处。&lt;/p&gt;
&lt;h2 id=&#34;mac-为什么看起来不容易爆&#34;&gt;Mac 为什么看起来“不容易爆”
&lt;/h2&gt;&lt;p&gt;Mac 这边最不一样的，不在模型，在内存结构。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Apple silicon&lt;/code&gt; 走的是统一内存。CPU 和 GPU 共享同一池内存，而不是像独显机器那样，一边是显存，一边是主内存，中间再靠总线搬。&lt;/p&gt;
&lt;p&gt;这套结构最大的好处，就是很多在独显机器上“显存根本装不下”的模型，到 Mac 上会变成另外一种状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不一定很快&lt;/li&gt;
&lt;li&gt;但大概率先能装进去&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，Mac 更不容易在第一步就撞上那堵非常生硬的“显存墙”。&lt;/p&gt;
&lt;p&gt;这就是为什么很多人会觉得，Mac 在本地大模型这件事上特别适合兜底。它解决的是“能不能先把整套工作集塞进同一池内存里”。&lt;/p&gt;
&lt;h2 id=&#34;但统一内存不等于送你高速度&#34;&gt;但统一内存不等于送你高速度
&lt;/h2&gt;&lt;p&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;/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;没有让所有推理框架自动拥有 CUDA 那套成熟生态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以 Mac 的舒服，和英伟达大显存卡的快，不是同一回事。&lt;/p&gt;
&lt;p&gt;Mac 更像是：&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;ul&gt;
&lt;li&gt;生态成熟&lt;/li&gt;
&lt;li&gt;CUDA 工具链完整&lt;/li&gt;
&lt;li&gt;真把模型和 cache 留在 GPU 里以后，速度更容易拉起来&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;为什么想要速度最后还是得看英伟达大显存&#34;&gt;为什么想要速度，最后还是得看英伟达大显存
&lt;/h2&gt;&lt;p&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;更高 token/s&lt;/li&gt;
&lt;li&gt;尽量减少等待&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那最后看的还是大显存英伟达卡。因为你真正在买的，是把模型和 cache 尽量稳定留在 GPU 那边的能力。&lt;/p&gt;
&lt;p&gt;Mac 当然也成立，但它更适合另外一类诉求：&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;/ul&gt;
&lt;p&gt;这两种路线都合理，只是解决的问题不一样。&lt;/p&gt;
&lt;h2 id=&#34;放回到-gemma-4我的最终判断&#34;&gt;放回到 Gemma 4，我的最终判断
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Gemma 4&lt;/code&gt; 这次确实让我觉得，本地开源模型已经进入了一个更值得认真讨论硬件的阶段。&lt;/p&gt;
&lt;p&gt;但模型变强，不代表物理规律跟着一起松了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;31B&lt;/code&gt; 再强，显存不够也会掉速。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;26B A4B&lt;/code&gt; 再务实，上下文过长也会吃压力。&lt;/p&gt;
&lt;p&gt;Mac 统一内存再舒服，也只是让“先跑起来”更容易，不会白送你一张大显存 CUDA 卡的速度。&lt;/p&gt;
&lt;p&gt;所以最后还是那句很土的话：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要速度，优先英伟达大显存&lt;/li&gt;
&lt;li&gt;要兜底，Mac 的统一内存确实舒服&lt;/li&gt;
&lt;li&gt;要在 &lt;code&gt;3060 12GB&lt;/code&gt; 这种机器上长期玩，别老惦记 dense 大模型，&lt;code&gt;26B A4B&lt;/code&gt; 这种路线更现实&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这组文章写到这里，也就收住了。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.google/innovation-and-ai/technology/developers-tools/gemma-4/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 4: Byte for byte, the most capable open models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ai.google.dev/gemma/docs/core/model_card_4&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 4 model card&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.apple.com/newsroom/2023/10/apple-unveils-m3-m3-pro-and-m3-max-the-most-advanced-chips-for-a-personal-computer/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Apple unveils M3, M3 Pro, and M3 Max&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.apple.com/newsroom/2023/01/apple-unveils-m2-pro-and-m2-max-next-generation-chips-for-next-level-workflows/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Apple unveils M2 Pro and M2 Max&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.apple.com/macbook-pro/specs/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;MacBook Pro Tech Specs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 谷歌时隔一年，发布了 Gemma4 模型，老规矩，尝试本地部署，还是那台没升级的台式 3060 12GB 英伟达显卡。这次赶上了首发，但是没找到以前常用 Gemma3 的升级版本，但是多有个类似的版本 GemmaE4b，你先搜索介绍下，本次发布了的所有型号，里面的缩写字母什么意思，然后搜索下网上关于 Gemma4 的评价，关键是，本次谷歌更新该了模型的协议，大家用起来的限制更少了。最大的惊喜，我常用的测试题：写一段 C++ 代码，在控制台输出五角星，去年的小参数开源模型都没搞定这个问题，谷歌这次搞定了，第一版给出答案，完全超出我的意料，它知道了我的陷阱，控制台输出五角星很麻烦，它直接硬编码了一个五角星的字符串，控制台直接输出。这是原文：由于在纯文本的控制台（Console）中通过数学逻辑直接绘制一个具有精确几何结构的五角星非常复杂（涉及到坐标系转换和像素填充），最经典且视觉效果最好的方法是使用 ASCII Art（字符艺术）。在我去强制要求进行计算以后，它也搞定了，通过数学计算，成功的绘制了五角星。以前常用 Gemma4 进行本地的翻译任务，当前博客很多历史文章的多语言版本就是这样来的。本地测试用的：gemma-4-26b-a4b 模型，31b 版本属实太慢了。但是看测评 31b 效果很不错，排行榜的成绩很好。同时刷论坛，我认知到了，显存如果不够，模型参数上去了，生成 token 的速度会断崖式下降，你解释下为什么？Mac 不会有这个问题，它走的是统一内存，解释下技术原因。还有就是，如果需要速度，那还是 英伟达大显存的显卡才行。Mac 的方案能兜底，但是速度上不去。本次的内容很多，你评估下是否拆成系列文章。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;第三篇只保留“速度为什么塌”和“Mac 为什么不等于快”这两条线，不再回头解释前两篇的内容。&lt;/li&gt;
&lt;li&gt;先讲显存边界，再讲非线性掉速，逻辑比上一版更顺。&lt;/li&gt;
&lt;li&gt;Mac 和英伟达被拆成两个不同维度来讲，一个偏兜底，一个偏速度。&lt;/li&gt;
&lt;li&gt;结尾只保留硬件判断，不再重复解释为什么拆系列。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Gemma 4 开放以后（二）：3060 12GB 的本地实测账</title>
        <link>https://ttf248.life/p/gemma-4-series-local-test-on-rtx-3060/</link>
        <pubDate>Wed, 08 Apr 2026 23:52:20 +0800</pubDate>
        
        <guid>https://ttf248.life/p/gemma-4-series-local-test-on-rtx-3060/</guid>
        <description>&lt;p&gt;如果只看榜单，最容易心动的肯定是 &lt;code&gt;31B&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但真把机器搬出来，还是那台没升级的 &lt;code&gt;RTX 3060 12GB&lt;/code&gt;，判断马上就会变。怎么说呢，本地部署这件事，最后拼的不是谁最风光，而是谁最像能长期相处的那个。对我来说，这次真正值得先跑的，不是 &lt;code&gt;31B&lt;/code&gt;，而是 &lt;code&gt;26B A4B&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;上一篇 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/gemma-4-series-models-and-license/&#34; &gt;谷歌这次把 Gemma 4 放开了（一）：先别急着跑本地，型号和协议得先看明白&lt;/a&gt; 把发布和协议讲完了。当前这一篇就只说本地体验本身；最后一篇接着写 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/gemma-4-series-vram-cliff-and-mac-unified-memory/&#34; &gt;谷歌这次把 Gemma 4 放开了（三）：显存不够为什么会断崖，Mac 为什么能兜底却快不起来&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;为什么我先跑-26b-a4b&#34;&gt;为什么我先跑 &lt;code&gt;26B A4B&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;原因其实很土，就是硬件现实。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;31B&lt;/code&gt; 当然强，官方榜单和社区第一波反馈都挺猛。但你把它放到 &lt;code&gt;3060 12GB&lt;/code&gt; 这种机器上，问题马上就不再是“它强不强”，而是“它值不值得你等”。一旦模型和 cache 往系统内存回退，速度很容易直接塌掉，这件事我放到第三篇详细讲。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;26B A4B&lt;/code&gt; 不一样。&lt;/p&gt;
&lt;p&gt;它虽然总参数是 &lt;code&gt;25.2B&lt;/code&gt;，但每个 token 真正激活的大约只有 &lt;code&gt;3.8B&lt;/code&gt;。说白了，它就是这次 Gemma 4 里最像“专门留给本地玩家”的那档。&lt;/p&gt;
&lt;p&gt;所以如果你的机器和我差不多，还是消费级老卡，判断可以直接一点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想看榜单，上 &lt;code&gt;31B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;想真本地长期用，先看 &lt;code&gt;26B A4B&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;五角星这题这次终于有人看懂我在挖坑&#34;&gt;五角星这题，这次终于有人看懂我在挖坑
&lt;/h2&gt;&lt;p&gt;我自己一直有个很土的测试题，让模型写一段 &lt;code&gt;C++&lt;/code&gt; 代码，在控制台输出五角星。&lt;/p&gt;
&lt;p&gt;这题看起来像玩笑，实际挺坏。因为很多模型会把它理解成一个纯数学绘图题，接着就开始上坐标、三角函数、循环，最后在纯文本控制台里输出一坨根本不能看的字符。&lt;/p&gt;
&lt;p&gt;去年很多小参数开源模型，基本都死在这里。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Gemma 4&lt;/code&gt; 这次第一反应，反而让我很意外。它没急着装懂，而是先识别约束，给出了这段判断：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;由于在纯文本的控制台（Console）中通过数学逻辑直接绘制一个具有精确几何结构的五角星非常复杂（涉及到坐标系转换和像素填充），最经典且视觉效果最好的方法是使用 ASCII Art（字符艺术）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;说白了，它先看懂了题目背后的环境限制。控制台不是画布，字符网格也不是像素网格。你要先把“怎么稳定给出一个五角星”想明白，再谈数学绘制。&lt;/p&gt;
&lt;p&gt;然后它第一版直接给了一个硬编码的五角星字符串。&lt;/p&gt;
&lt;p&gt;这个动作特别对味。不是为了秀推导，而是先把题做对。&lt;/p&gt;
&lt;h2 id=&#34;更让我意外的是它还能继续往下走&#34;&gt;更让我意外的是，它还能继续往下走
&lt;/h2&gt;&lt;p&gt;如果只是停在 ASCII Art，这题还只能算它识别了陷阱。&lt;/p&gt;
&lt;p&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;/ul&gt;
&lt;p&gt;以前很多本地小模型一上来就冲第二层，最后第一层都没做好。&lt;code&gt;Gemma 4&lt;/code&gt; 这次反过来了，先把边界认出来，再决定怎么解。&lt;/p&gt;
&lt;p&gt;我觉得这件事比单独一项 benchmark 分数更值钱。&lt;/p&gt;
&lt;h2 id=&#34;这次-coding-提升不只是更聪明了&#34;&gt;这次 coding 提升，不只是“更聪明了”
&lt;/h2&gt;&lt;p&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;/ul&gt;
&lt;p&gt;这种题一旦能做对，说明模型开始更像一个会处理现实约束的开发助手，而不是只会补全代码片段。&lt;/p&gt;
&lt;p&gt;我对 &lt;code&gt;Gemma 4&lt;/code&gt; 的第一印象会比去年那批小参数开源模型好很多。去年很多模型属于能聊天、能补全、能凑合，但一碰到这种稍微带点边界感的问题，就容易露底。&lt;/p&gt;
&lt;p&gt;这次谷歌起码把这个短板补上了。&lt;/p&gt;
&lt;h2 id=&#34;翻译这条线反而不能简单说gemma-4-全面接班&#34;&gt;翻译这条线，反而不能简单说“Gemma 4 全面接班”
&lt;/h2&gt;&lt;p&gt;你前面提到一个点很关键，以前常拿 &lt;code&gt;Gemma&lt;/code&gt; 跑本地翻译。&lt;/p&gt;
&lt;p&gt;这件事到 &lt;code&gt;Gemma 4&lt;/code&gt; 这里，其实没那么线性。因为谷歌在 2026 年 2 月单独发了 &lt;code&gt;TranslateGemma&lt;/code&gt;，而且还是建在 &lt;code&gt;Gemma 3&lt;/code&gt; 那套尺寸上。&lt;/p&gt;
&lt;p&gt;这意味着什么？&lt;/p&gt;
&lt;p&gt;意味着如果你现成的本地翻译链路已经跑顺了，短期内不一定非要全部切到 &lt;code&gt;Gemma 4&lt;/code&gt;。尤其是那种目标特别单一、只想稳定多语言转换的场景，专门的翻译模型还是有它的价值。&lt;/p&gt;
&lt;p&gt;但如果你想要的是一套本地模型，尽量兼顾翻译、问答、代码和一般文本任务，那 &lt;code&gt;26B A4B&lt;/code&gt; 这种更全能的路线就很顺。&lt;/p&gt;
&lt;p&gt;它未必是最专的，但它更像现实世界里“只想先跑起来一个够用主力模型”的选择。&lt;/p&gt;
&lt;h2 id=&#34;为什么我不想在第二篇里继续吹-31b&#34;&gt;为什么我不想在第二篇里继续吹 &lt;code&gt;31B&lt;/code&gt;
&lt;/h2&gt;&lt;p&gt;不是因为 &lt;code&gt;31B&lt;/code&gt; 不行，恰恰相反，它太行了，所以很容易把注意力带偏。&lt;/p&gt;
&lt;p&gt;你一旦一直盯着 &lt;code&gt;31B&lt;/code&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;3060 12GB&lt;/code&gt; 这种机器上，这些现实问题比榜单重要多了。&lt;/p&gt;
&lt;p&gt;所以我对第二篇的收口很简单。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;31B&lt;/code&gt; 值得看，&lt;code&gt;26B A4B&lt;/code&gt; 值得用。对本地玩家来说，这两句话不是一回事。&lt;/p&gt;
&lt;h2 id=&#34;我的本地第一结论&#34;&gt;我的本地第一结论
&lt;/h2&gt;&lt;p&gt;如果让我用一句话概括这次实测感受，那就是：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Gemma 4&lt;/code&gt; 终于开始像一个会看场景的本地模型了。&lt;/p&gt;
&lt;p&gt;尤其是 &lt;code&gt;26B A4B&lt;/code&gt;。它不是那种最能拿来晒榜单的型号，但在老机器、消费级显卡、本地长期使用这些现实约束下，它反而更像真正的主力选择。&lt;/p&gt;
&lt;p&gt;至少这次五角星测试，谷歌是过关了。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.google/innovation-and-ai/technology/developers-tools/gemma-4/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 4: Byte for byte, the most capable open models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ai.google.dev/gemma/docs/core/model_card_4&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 4 model card&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://huggingface.co/google/gemma-4-26B-A4B-it&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;google/gemma-4-26B-A4B-it on Hugging Face&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.googleblog.com/introducing-gemma3/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 3: The Developer Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.google/innovation-and-ai/technology/developers-tools/translategemma/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;TranslateGemma: A new family of open translation models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://foodtruckbench.com/blog/gemma-4-31b&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 4 31B on FoodTruck Bench&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 谷歌时隔一年，发布了 Gemma4 模型，老规矩，尝试本地部署，还是那台没升级的台式 3060 12GB 英伟达显卡。这次赶上了首发，但是没找到以前常用 Gemma3 的升级版本，但是多有个类似的版本 GemmaE4b，你先搜索介绍下，本次发布了的所有型号，里面的缩写字母什么意思，然后搜索下网上关于 Gemma4 的评价，关键是，本次谷歌更新该了模型的协议，大家用起来的限制更少了。最大的惊喜，我常用的测试题：写一段 C++ 代码，在控制台输出五角星，去年的小参数开源模型都没搞定这个问题，谷歌这次搞定了，第一版给出答案，完全超出我的意料，它知道了我的陷阱，控制台输出五角星很麻烦，它直接硬编码了一个五角星的字符串，控制台直接输出。这是原文：由于在纯文本的控制台（Console）中通过数学逻辑直接绘制一个具有精确几何结构的五角星非常复杂（涉及到坐标系转换和像素填充），最经典且视觉效果最好的方法是使用 ASCII Art（字符艺术）。在我去强制要求进行计算以后，它也搞定了，通过数学计算，成功的绘制了五角星。以前常用 Gemma4 进行本地的翻译任务，当前博客很多历史文章的多语言版本就是这样来的。本地测试用的：gemma-4-26b-a4b 模型，31b 版本属实太慢了。但是看测评 31b 效果很不错，排行榜的成绩很好。同时刷论坛，我认知到了，显存如果不够，模型参数上去了，生成 token 的速度会断崖式下降，你解释下为什么？Mac 不会有这个问题，它走的是统一内存，解释下技术原因。还有就是，如果需要速度，那还是 英伟达大显存的显卡才行。Mac 的方案能兜底，但是速度上不去。本次的内容很多，你评估下是否拆成系列文章。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;第二篇只保留本地体验，不再替第一篇做总述，也不替第三篇讲显存原理。&lt;/li&gt;
&lt;li&gt;先给出“为什么先跑 26B A4B”的硬判断，再展开五角星测试。&lt;/li&gt;
&lt;li&gt;五角星题被当成主轴，是因为它比跑分更能说明 coding 场景里的边界感。&lt;/li&gt;
&lt;li&gt;翻译任务单独收一节，避免把 &lt;code&gt;Gemma 4&lt;/code&gt; 写成对所有旧流程的线性接班。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Gemma 4 开放以后（一）：型号和许可证先落地</title>
        <link>https://ttf248.life/p/gemma-4-series-models-and-license/</link>
        <pubDate>Wed, 08 Apr 2026 23:48:20 +0800</pubDate>
        
        <guid>https://ttf248.life/p/gemma-4-series-models-and-license/</guid>
        <description>&lt;p&gt;首发当天我本来想干的事很简单，找一个和 &lt;code&gt;Gemma 3&lt;/code&gt; 对应得上的升级版，先下下来跑。&lt;/p&gt;
&lt;p&gt;结果一圈看下来，人先有点傻眼。以前熟的 &lt;code&gt;4B / 12B / 27B&lt;/code&gt; 那套名字没了，冒出来的是 &lt;code&gt;E4B&lt;/code&gt;、&lt;code&gt;26B A4B&lt;/code&gt;、&lt;code&gt;31B&lt;/code&gt;。怎么说呢，这次谷歌真正改的，不只是模型大小，而是连“你该怎么理解这批模型”都一起改了。&lt;/p&gt;
&lt;p&gt;这组文章我拆成三篇。当前这一篇只把发布信息、型号和协议讲清楚；下一篇写 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/gemma-4-series-local-test-on-rtx-3060/&#34; &gt;谷歌这次把 Gemma 4 放开了（二）：3060 12GB 本地跑下来，26B A4B 才是更现实的那个&lt;/a&gt;；最后一篇收在 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/gemma-4-series-vram-cliff-and-mac-unified-memory/&#34; &gt;谷歌这次把 Gemma 4 放开了（三）：显存不够为什么会断崖，Mac 为什么能兜底却快不起来&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;先把这次到底发了什么说清楚&#34;&gt;先把这次到底发了什么说清楚
&lt;/h2&gt;&lt;p&gt;去年 &lt;code&gt;Gemma 3&lt;/code&gt; 是 2025 年 3 月 12 日发布的，这次 &lt;code&gt;Gemma 4&lt;/code&gt; 是 2026 年 4 月 2 日发布，确实差不多隔了一年。&lt;/p&gt;
&lt;p&gt;但这次不能再按“27B 的下一代是谁”这种思路去找。官方给的四个主要尺寸，已经不是单纯按总参数分档了。&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;&lt;code&gt;E2B&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Dense&lt;/td&gt;
					&lt;td&gt;2.3B effective，5.1B 含 embeddings，128K context&lt;/td&gt;
					&lt;td&gt;设备侧、超轻量本地&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;E4B&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Dense&lt;/td&gt;
					&lt;td&gt;4.5B effective，8B 含 embeddings，128K context&lt;/td&gt;
					&lt;td&gt;原来 4B 这条小模型主线&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;26B A4B&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;MoE&lt;/td&gt;
					&lt;td&gt;25.2B total，约 3.8B active，256K context&lt;/td&gt;
					&lt;td&gt;消费级显卡、本地部署、兼顾质量和速度&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;31B&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Dense&lt;/td&gt;
					&lt;td&gt;30.7B dense，256K context&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;ul&gt;
&lt;li&gt;小模型设备侧，给 &lt;code&gt;E2B / E4B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;本地玩家路线，给 &lt;code&gt;26B A4B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;质量和上限路线，给 &lt;code&gt;31B&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这也是为什么很多人首发第一感觉会是“以前熟的升级路径断了”。不是没给升级版，是谷歌不想再只按总参数一个维度来卖货。&lt;/p&gt;
&lt;h2 id=&#34;e-和-a-这次不是装饰字母&#34;&gt;&lt;code&gt;E&lt;/code&gt; 和 &lt;code&gt;A&lt;/code&gt; 这次不是装饰字母
&lt;/h2&gt;&lt;p&gt;这批名字里，最容易让人犯嘀咕的就是 &lt;code&gt;E4B&lt;/code&gt; 和 &lt;code&gt;A4B&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;E2B&lt;/code&gt;、&lt;code&gt;E4B&lt;/code&gt; 里的 &lt;code&gt;E&lt;/code&gt;，官方给的是 &lt;code&gt;effective parameters&lt;/code&gt;。因为这两个模型用了 &lt;code&gt;Per-Layer Embeddings&lt;/code&gt;，所以总参数量和真正有效参数量不是一个口径。说白了，谷歌是在提醒你，这不是过去那种“一个朴素的 4B dense 模型”。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;26B A4B&lt;/code&gt; 里的 &lt;code&gt;A&lt;/code&gt;，就是 &lt;code&gt;active parameters&lt;/code&gt;。总盘子是 &lt;code&gt;25.2B&lt;/code&gt;，但每个 token 实际激活的大约是 &lt;code&gt;3.8B&lt;/code&gt;。这就是 MoE 路线的关键，模型总量不小，但运行时真正参与计算的那部分小很多。&lt;/p&gt;
&lt;p&gt;所以这两个名字看着都带个 &lt;code&gt;4B&lt;/code&gt;，含义却完全不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;E4B&lt;/code&gt; 是小模型主线&lt;/li&gt;
&lt;li&gt;&lt;code&gt;26B A4B&lt;/code&gt; 是大盘子 MoE，本地推理时更像“激活规模只有 4B 左右”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个命名方式一开始确实别扭，但它比过去更接近真实部署体验。&lt;/p&gt;
&lt;h2 id=&#34;如果你以前常用-gemma-3这次该怎么找对应关系&#34;&gt;如果你以前常用 Gemma 3，这次该怎么找对应关系
&lt;/h2&gt;&lt;p&gt;我觉得这一代最容易误判的地方，就是把它当成 &lt;code&gt;Gemma 3&lt;/code&gt; 的线性升级。&lt;/p&gt;
&lt;p&gt;如果按使用习惯去找，差不多可以这么理解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以前盯着 &lt;code&gt;4B&lt;/code&gt; 跑轻任务的人，现在先看 &lt;code&gt;E4B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;以前盯着 &lt;code&gt;27B&lt;/code&gt; 看模型上限的人，现在看 &lt;code&gt;31B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;以前想在消费级显卡上找一个“够强但不至于完全跑不动”的平衡点，现在重点看 &lt;code&gt;26B A4B&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这一层不先理顺，后面本地部署很容易跑偏。你会一边吐槽“怎么没有熟悉的升级版”，一边又把真正适合自己的那个型号错过去。&lt;/p&gt;
&lt;h2 id=&#34;这次最值钱的更新其实不是参数&#34;&gt;这次最值钱的更新，其实不是参数
&lt;/h2&gt;&lt;p&gt;真正让我觉得这次发布像是“终于想通了”的，不是榜单，而是协议。&lt;/p&gt;
&lt;p&gt;老版本 &lt;code&gt;Gemma&lt;/code&gt; 那套 terms 不能说完全没法用，但一直有点别扭。尤其是你如果关心这些事：&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;你总得回头看看条款里那些 notice、下游限制、附带协议到底要怎么处理。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Gemma 4&lt;/code&gt; 这次直接改成 &lt;code&gt;Apache 2.0&lt;/code&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;义务主要回到保留 license、notice、修改说明这些开源世界熟悉的东西&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;说白了，谷歌这次不是单纯把模型开源了点，而是把“大家到底敢不敢放心拿去用”这件事一起做顺了。&lt;/p&gt;
&lt;h2 id=&#34;社区第一波评价基本也是两条线&#34;&gt;社区第一波评价，基本也是两条线
&lt;/h2&gt;&lt;p&gt;第一条线是，&lt;code&gt;31B&lt;/code&gt; 确实能打。&lt;/p&gt;
&lt;p&gt;官方给出的成绩已经很猛了。&lt;code&gt;Arena AI&lt;/code&gt; 文本榜单里，31B 发布时排到开源模型前列，&lt;code&gt;LiveCodeBench v6&lt;/code&gt; 也明显比 &lt;code&gt;Gemma 3 27B&lt;/code&gt; 上去了一大截。很多人看到的第一反应就是，这个尺寸能打成这样，挺超预期。&lt;/p&gt;
&lt;p&gt;第二条线是，&lt;code&gt;26B A4B&lt;/code&gt; 很像给本地玩家留的一条活路。&lt;/p&gt;
&lt;p&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;/ul&gt;
&lt;h2 id=&#34;我对第一篇的结论&#34;&gt;我对第一篇的结论
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Gemma 4&lt;/code&gt; 不再是“从小到大一排 dense 模型”的老思路，而是把设备侧、本地部署、质量上限这三条路分开了。&lt;code&gt;E4B&lt;/code&gt;、&lt;code&gt;26B A4B&lt;/code&gt;、&lt;code&gt;31B&lt;/code&gt; 看着名字怪，背后对应的却是很现实的部署分工。&lt;/p&gt;
&lt;p&gt;但如果你问我这次最大的变化到底是什么，我还是那句判断：&lt;/p&gt;
&lt;p&gt;不是参数，不是榜单，而是谷歌终于把 &lt;code&gt;Gemma 4&lt;/code&gt; 放进了一个大家更敢真用的开源协议里。&lt;/p&gt;
&lt;p&gt;这一步，比表上的数字更重要。&lt;/p&gt;
&lt;p&gt;下一篇我就不继续讲发布会口径了，直接回到本地机器上。还是那张没升级的 &lt;code&gt;RTX 3060 12GB&lt;/code&gt;，为什么我最后先盯上的不是 &lt;code&gt;31B&lt;/code&gt;，而是 &lt;code&gt;26B A4B&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://blog.google/innovation-and-ai/technology/developers-tools/gemma-4/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 4: Byte for byte, the most capable open models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ai.google.dev/gemma/docs/core/model_card_4&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 4 model card&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ai.google.dev/gemma/terms&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma Terms of Use&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ai.google.dev/gemma/apache_2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Apache License 2.0 for Gemma 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://foodtruckbench.com/blog/gemma-4-31b&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 4 31B on FoodTruck Bench&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.reddit.com/r/LocalLLaMA/comments/1san4kd/will_gemma_4_124b_moe_open_as_well/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;LocalLLaMA discussion on Gemma 4 license changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.googleblog.com/introducing-gemma3/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 3: The Developer Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 谷歌时隔一年，发布了 Gemma4 模型，老规矩，尝试本地部署，还是那台没升级的台式 3060 12GB 英伟达显卡。这次赶上了首发，但是没找到以前常用 Gemma3 的升级版本，但是多有个类似的版本 GemmaE4b，你先搜索介绍下，本次发布了的所有型号，里面的缩写字母什么意思，然后搜索下网上关于 Gemma4 的评价，关键是，本次谷歌更新该了模型的协议，大家用起来的限制更少了。最大的惊喜，我常用的测试题：写一段 C++ 代码，在控制台输出五角星，去年的小参数开源模型都没搞定这个问题，谷歌这次搞定了，第一版给出答案，完全超出我的意料，它知道了我的陷阱，控制台输出五角星很麻烦，它直接硬编码了一个五角星的字符串，控制台直接输出。这是原文：由于在纯文本的控制台（Console）中通过数学逻辑直接绘制一个具有精确几何结构的五角星非常复杂（涉及到坐标系转换和像素填充），最经典且视觉效果最好的方法是使用 ASCII Art（字符艺术）。在我去强制要求进行计算以后，它也搞定了，通过数学计算，成功的绘制了五角星。以前常用 Gemma4 进行本地的翻译任务，当前博客很多历史文章的多语言版本就是这样来的。本地测试用的：gemma-4-26b-a4b 模型，31b 版本属实太慢了。但是看测评 31b 效果很不错，排行榜的成绩很好。同时刷论坛，我认知到了，显存如果不够，模型参数上去了，生成 token 的速度会断崖式下降，你解释下为什么？Mac 不会有这个问题，它走的是统一内存，解释下技术原因。还有就是，如果需要速度，那还是 英伟达大显存的显卡才行。Mac 的方案能兜底，但是速度上不去。本次的内容很多，你评估下是否拆成系列文章。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&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;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>AI 博客工程（三）：本地、在线和 MiniMax 怎么分工</title>
        <link>https://ttf248.life/p/how-i-split-local-online-and-minimax-models/</link>
        <pubDate>Fri, 03 Apr 2026 21:06:02 +0800</pubDate>
        
        <guid>https://ttf248.life/p/how-i-split-local-online-and-minimax-models/</guid>
        <description>&lt;p&gt;翻了一圈现在仓库里的配置，我反而更确定一件事：这套东西最后拼的不是单个模型有多强，而是每一层到底该让谁来承担成本。&lt;/p&gt;
&lt;p&gt;最明显的一个信号就是，当前生效的 &lt;code&gt;published.runtime.json&lt;/code&gt; 还是 2026 年 4 月 2 日生成的 &lt;code&gt;minimax-m2&lt;/code&gt;，但 2026 年 4 月 3 日 16:38 的 &lt;code&gt;5f17088&lt;/code&gt; 已经把 &lt;code&gt;blog-style-suite&lt;/code&gt; 的默认 provider 切到了本地 &lt;code&gt;LM Studio&lt;/code&gt; 里的 &lt;code&gt;gemma-4-26b-a4b&lt;/code&gt;。这看起来像前后不一致，其实不是，它恰好说明了这条流水线开始有了分工。&lt;/p&gt;
&lt;p&gt;这组文章到这里，前两篇已经把边界铺开了。&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/why-blog-writer-had-to-exist/&#34; &gt;第一篇&lt;/a&gt; 讲的是 &lt;code&gt;blog-writer&lt;/code&gt; 为什么会长出来，&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/how-blog-style-suite-split-style-and-token-cost/&#34; &gt;第二篇&lt;/a&gt; 讲的是 &lt;code&gt;blog-style-suite&lt;/code&gt; 怎么把风格学习和 token 成本拆开。最后这一篇，就收在最现实的问题上：本地模型、在线模型、&lt;code&gt;Minimax&lt;/code&gt;，到底该放在哪个工位上。&lt;/p&gt;
&lt;h2 id=&#34;训练风格数据不值得每一步都烧在线模型&#34;&gt;训练风格数据，不值得每一步都烧在线模型
&lt;/h2&gt;&lt;p&gt;风格数据这件事，一旦开始认真做，token 很快就会变成现实问题。&lt;/p&gt;
&lt;p&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;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;p&gt;反过来想，哪些步骤必须在线，哪些步骤其实应该尽量本地化，哪些步骤甚至根本不该交给模型。&lt;/p&gt;
&lt;p&gt;只要这个边界不清楚，再强的模型进来，最后也只是在帮你重复做一堆本来能预处理掉的活。&lt;/p&gt;
&lt;h2 id=&#34;本地模型更适合脏活重活和反复试错&#34;&gt;本地模型更适合脏活、重活和反复试错
&lt;/h2&gt;&lt;p&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;/p&gt;
&lt;p&gt;不是单次价值极高，而是要反复跑、能容忍试错、并且最好别每一轮都重新付高价。&lt;/p&gt;
&lt;p&gt;当前 &lt;code&gt;scripts/blog-style-suite/config.json&lt;/code&gt; 已经切到了 &lt;code&gt;lm-studio-gemma4&lt;/code&gt;，这本身就说明判断在变。不是说本地 &lt;code&gt;gemma&lt;/code&gt; 一定比在线模型更强，而是生产侧这条链路，终于开始优先考虑“跑得起、跑得勤、能反复改”。&lt;/p&gt;
&lt;p&gt;这一点，其实和我前面写过的 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/weaker-models-shouldnt-do-frontier-work/&#34; &gt;弱模型别硬上强活&lt;/a&gt; 是同一个逻辑。&lt;/p&gt;
&lt;p&gt;本地模型不一定适合总包复杂写稿，但很适合接那些脏活、重活、批量活。风格数据的预处理，本来就更像这一类任务。&lt;/p&gt;
&lt;h2 id=&#34;在线模型更适合收口不适合包办一切&#34;&gt;在线模型更适合收口，不适合包办一切
&lt;/h2&gt;&lt;p&gt;说本地模型适合生产侧，不等于在线模型就没价值了。&lt;/p&gt;
&lt;p&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;/p&gt;
&lt;p&gt;也就是说，强模型更像总装线最后那几道工序。它不是不可以往前多干点，但如果你让它从头扫到尾，整个成本结构很快就会走形。&lt;/p&gt;
&lt;p&gt;这也是为什么 &lt;code&gt;blog-writer&lt;/code&gt; 在设计上只读发布位 &lt;code&gt;published.runtime.json&lt;/code&gt;，而不是写稿时再去切 provider、再去回扫 suite 目录。消费侧越轻，越适合让更强的模型专心把文章收好。&lt;/p&gt;
&lt;h2 id=&#34;minimax-的意义不只是多接了一个-provider&#34;&gt;Minimax 的意义，不只是多接了一个 provider
&lt;/h2&gt;&lt;p&gt;很多人看到 &lt;code&gt;Minimax&lt;/code&gt;，第一反应可能是：无非又多接了一个模型。&lt;/p&gt;
&lt;p&gt;我觉得不是。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Minimax&lt;/code&gt; 真正有价值的地方，是它把“多 provider 输出，同一发布契约消费”这条路走通了。&lt;/p&gt;
&lt;p&gt;2026 年 4 月 2 日 10:18 的 &lt;code&gt;9f15199&lt;/code&gt; 把 &lt;code&gt;blog-style-suite&lt;/code&gt; 改成了多模型配置，输出按 provider 隔离。后面 README 和 runtime 结构也一直在强调一件事：suite 可以生成很多份结果，但真正生效的只有人工挑出来的 &lt;code&gt;published.runtime.json&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这个边界特别重要。&lt;/p&gt;
&lt;p&gt;因为一旦边界明确了，&lt;code&gt;Minimax&lt;/code&gt; 的角色就不再是“必须绑定在写稿流程里”，而变成了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它可以参与生产侧的对比&lt;/li&gt;
&lt;li&gt;可以用来生成一版 runtime&lt;/li&gt;
&lt;li&gt;可以和本地模型产物横向比较&lt;/li&gt;
&lt;li&gt;最后由人工决定哪一版发布&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就把 provider 从“系统依赖”变成了“可替换部件”。&lt;/p&gt;
&lt;p&gt;我觉得这是 &lt;code&gt;Minimax&lt;/code&gt; 在这套工程里最有意思的意义。它不是来统治整条链路的，它是来验证这条链路到底有没有把接口收干净。&lt;/p&gt;
&lt;h2 id=&#34;真正的分工不是按模型强弱分是按任务类型分&#34;&gt;真正的分工，不是按模型强弱分，是按任务类型分
&lt;/h2&gt;&lt;p&gt;我现在更认同一种很土，但很管用的划分法。&lt;/p&gt;
&lt;h3 id=&#34;规则和硬约束&#34;&gt;规则和硬约束
&lt;/h3&gt;&lt;p&gt;交给本地脚本。&lt;/p&gt;
&lt;p&gt;能用 &lt;code&gt;scanner.py&lt;/code&gt;、&lt;code&gt;write_post.py&lt;/code&gt;、&lt;code&gt;write_post_series.py&lt;/code&gt; 这种确定性工具解决的，就别让模型掺和。&lt;/p&gt;
&lt;h3 id=&#34;风格数据生产&#34;&gt;风格数据生产
&lt;/h3&gt;&lt;p&gt;优先交给本地模型或成本更低的 provider。&lt;/p&gt;
&lt;p&gt;因为这里最重要的是可重复、可试错、可缓存，不是单次输出必须最华丽。&lt;/p&gt;
&lt;h3 id=&#34;最终写稿和事实收口&#34;&gt;最终写稿和事实收口
&lt;/h3&gt;&lt;p&gt;交给更适合长上下文整合、表达收束、联网补事实的模型。&lt;/p&gt;
&lt;p&gt;这一层才是在线模型最值得花钱的地方。&lt;/p&gt;
&lt;p&gt;这么一拆，很多原来纠结的问题反而没那么复杂了。你不需要每天争论“到底哪个模型最强”，你只需要问一句：这个任务属于哪一层。&lt;/p&gt;
&lt;h2 id=&#34;到最后最值钱的不是模型而是边界清楚&#34;&gt;到最后，最值钱的不是模型，而是边界清楚
&lt;/h2&gt;&lt;p&gt;第三篇我就收在这里。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;blog-writer&lt;/code&gt; 和 &lt;code&gt;blog-style-suite&lt;/code&gt; 这套东西一路演化下来，我觉得最值钱的，不是又接了谁、又换了谁、又试了哪个 provider。&lt;/p&gt;
&lt;p&gt;最值钱的是边界终于越来越清楚了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;blog-writer&lt;/code&gt; 管消费侧&lt;/li&gt;
&lt;li&gt;&lt;code&gt;blog-style-suite&lt;/code&gt; 管生产侧&lt;/li&gt;
&lt;li&gt;&lt;code&gt;published.runtime.json&lt;/code&gt; 是发布位&lt;/li&gt;
&lt;li&gt;本地模型更适合反复跑的脏活和重活&lt;/li&gt;
&lt;li&gt;在线模型更适合最后的收口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Minimax&lt;/code&gt; 这类在线 provider 更像可替换部件，而不是系统中枢&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;边界一清楚，整个工作流就顺了。&lt;/p&gt;
&lt;p&gt;你不会再指望一个模型包打天下，也不会再把每一步都堆到最贵那一层去做。到最后，这件事看起来是在选模型，实际上是在给不同类型的任务安排工位。&lt;/p&gt;
&lt;p&gt;说白了，单点更强当然好。&lt;/p&gt;
&lt;p&gt;但长期跑下来，边界清楚，往往比单点更强更重要。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/9f1519967981c5eef7bd1eb407b0406ac542ebd0&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;9f1519967981c5eef7bd1eb407b0406ac542ebd0&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/5f17088391ee858b88fc50df884bc0103ff0b3c1&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;5f17088391ee858b88fc50df884bc0103ff0b3c1&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库文件：&lt;code&gt;scripts/blog-style-suite/config.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;生效运行时：&lt;code&gt;.agents/data/blog-writing/published.runtime.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;相关旧文：&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/a-long-period-of-deep-ai-programming/&#34; &gt;重度AI编程的一段日子&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;相关旧文：&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/ultimately-its-returning-to-domestic-models/&#34; &gt;终归还是回到国产模型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;相关旧文：&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/weaker-models-shouldnt-do-frontier-work/&#34; &gt;弱模型别硬上强活&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 本次的内容比较多，拆分成系列文章：去年就有很多稿子是通过大模型写的，那会是自己写个大纲或者问题清单，然后AI出稿子，复制内容到本地 md 文档，填写头信息，标签信息、发布文章；近期 codex 用了很多，发现 codex 里面的联网搜索能力很强，那我是不是能写个 skill，将这些事情自动化，此时诞生了 skill blog-writer 的第一稿，我还想着让 AI 学习我以前文章的风格，这就导致 blog-writer 运行的时候，很费 token，后续我针对 blog-writer 进行了多个版本的优化，拆分了 数据模块，数据生成的模块，原本数据生成的模块还是独立的 skill，写着写着，我就发现，更适合做成 Python 项目，此时就有了 blog-style-suite，然后我又发现，训练风格数据，也是比较费 token，我就想着用本地的大模型，对接了本地的大模型，我又想到了对比下本地大模型和在线版本的区别，又对接了 minimax；blog-style-suite 和 blog-writer 的演化历史可以分析的 git 提价记录。顺带基于本地 blog-writer、blog-style-suite 的代码，可以讲讲里面的设计思路，是如何做到了节约 token，数据结构是如何设计的，核心的设计思路。Token 富裕完全能生吃历史文章，预处理能节约很多 token&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;第三篇不再重复讲架构，而是把“模型分工”这个现实问题单独收口。&lt;/li&gt;
&lt;li&gt;直接用当前仓库里 &lt;code&gt;published.runtime.json&lt;/code&gt; 还是 &lt;code&gt;minimax-m2&lt;/code&gt;、&lt;code&gt;config.json&lt;/code&gt; 已切到本地 &lt;code&gt;gemma4&lt;/code&gt; 这个事实开篇，减少空话。&lt;/li&gt;
&lt;li&gt;重点不是证明谁更强，而是说明为什么不同任务该由不同成本层来承担。&lt;/li&gt;
&lt;li&gt;把 &lt;code&gt;Minimax&lt;/code&gt; 放在“可替换 provider”这个位置上讲，是为了把它的意义拉回工程边界，而不是模型榜单。&lt;/li&gt;
&lt;li&gt;结尾回到“边界清楚比单点更强更重要”这个总判断，作为整组文章的收口。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>AI 博客工程（二）：把风格学习和 token 成本拆开</title>
        <link>https://ttf248.life/p/how-blog-style-suite-split-style-and-token-cost/</link>
        <pubDate>Fri, 03 Apr 2026 21:02:02 +0800</pubDate>
        
        <guid>https://ttf248.life/p/how-blog-style-suite-split-style-and-token-cost/</guid>
        <description>&lt;p&gt;如果 token 足够，最省脑子的办法其实很粗暴：把历史文章直接塞给模型，让它自己学。&lt;/p&gt;
&lt;p&gt;问题在于，这种办法只适合偶尔来一篇，不适合反复写。你要是真把博客写作当成长期工作流来做，生吃历史文章这条路，很快就会从“简单直接”变成“又贵又乱”。&lt;/p&gt;
&lt;p&gt;这组文章讲到这里，主线已经变了。上一篇 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/why-blog-writer-had-to-exist/&#34; &gt;AI 写博客这件事，后来还是得做成工程（一）：blog-writer 为什么会长出来&lt;/a&gt; 讲的是消费侧的自动化；这一篇开始讲生产侧，也就是风格数据怎么生成、怎么压缩、怎么别把 token 白白烧掉；下一篇会接着收在 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/how-i-split-local-online-and-minimax-models/&#34; &gt;AI 写博客这件事，后来还是得做成工程（三）：本地模型、在线模型和 Minimax 最后怎么分工&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;一开始最自然的想法就是直接喂历史文章&#34;&gt;一开始最自然的想法，就是直接喂历史文章
&lt;/h2&gt;&lt;p&gt;这条路真的太自然了。&lt;/p&gt;
&lt;p&gt;你既然想让模型学会你的写法，那最直观的办法当然就是把旧文章喂给它。最好把历史博客里写得最像自己的那些都塞进去，让它自己总结。&lt;/p&gt;
&lt;p&gt;单看一次任务，这么干没什么毛病。&lt;/p&gt;
&lt;p&gt;甚至很多时候效果还不错。上下文够长，模型够强，历史文章够多，风格确实能被带出来。&lt;/p&gt;
&lt;p&gt;但问题不是“这一篇能不能写成”，问题是“下一篇、下下篇，还要不要再来一遍”。&lt;/p&gt;
&lt;p&gt;每次都重新喂一批旧文章，会带来几个很现实的副作用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同一批材料反复占上下文&lt;/li&gt;
&lt;li&gt;token 开销和写稿次数近乎线性增长&lt;/li&gt;
&lt;li&gt;模型看到的噪音越来越多，真正有用的信号反而被稀释&lt;/li&gt;
&lt;li&gt;写稿动作和风格维护动作彻底绑死，谁都轻不下来&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，token 富裕的时候，生吃当然能吃。但工程上不能老这么吃。&lt;/p&gt;
&lt;h2 id=&#34;这也是为什么数据模块和数据生成模块必须拆开&#34;&gt;这也是为什么数据模块和数据生成模块必须拆开
&lt;/h2&gt;&lt;p&gt;我后来把这件事想明白，核心就一句话：消费侧和生产侧得分开。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;blog-writer&lt;/code&gt; 负责的是消费侧。它只管读一份已经发布好的 runtime，然后把文章按固定契约写出来。&lt;/p&gt;
&lt;p&gt;而风格数据的扫描、筛选、评分、压缩、provider 对比，这些都应该放到另一条生产侧流水线里。也就是后来长出来的 &lt;code&gt;blog-style-suite&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;从 git 记录看，这个转折很清楚。&lt;/p&gt;
&lt;p&gt;2026 年 4 月 1 日 21:47 的 &lt;code&gt;84a06b5&lt;/code&gt;，已经明确把原来的 &lt;code&gt;blog-style-maintainer&lt;/code&gt; skill 换成了仓库级 CLI 工具。这个动作很说明问题，因为一旦你有了 &lt;code&gt;scan/build/rebuild&lt;/code&gt;、有了输出目录、有了恢复机制，事情就已经不像一个 skill 了，更像一个正常的 Python 项目。&lt;/p&gt;
&lt;p&gt;再到 2026 年 4 月 1 日 23:05 的 &lt;code&gt;9e92b8e&lt;/code&gt;，&lt;code&gt;blog-style-suite&lt;/code&gt; 继续被拆成 &lt;code&gt;scanner.py&lt;/code&gt;、&lt;code&gt;builder.py&lt;/code&gt;、&lt;code&gt;compressor.py&lt;/code&gt; 这些模块。到这一步，思路其实已经非常工程化了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scanner.py&lt;/code&gt; 负责把文章从磁盘里扫出来，提取结构化特征&lt;/li&gt;
&lt;li&gt;&lt;code&gt;builder.py&lt;/code&gt; 负责评分、精选、缓存、运行时装配&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compressor.py&lt;/code&gt; 负责该让模型参与的那几步压缩&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这和单纯写一段超级 prompt，已经是两种完全不同的思路了。&lt;/p&gt;
&lt;h2 id=&#34;省-token不靠玄学靠的是预处理和批量化&#34;&gt;省 token，不靠玄学，靠的是预处理和批量化
&lt;/h2&gt;&lt;p&gt;2026 年 4 月 2 日 19:41 的 &lt;code&gt;bc4b950&lt;/code&gt; 是关键的一次提交。&lt;/p&gt;
&lt;p&gt;那次提交把话说得很直白，AI 调用从大约 &lt;code&gt;2000&lt;/code&gt; 次，直接降到了每个 provider 最多 &lt;code&gt;5&lt;/code&gt; 次。&lt;/p&gt;
&lt;p&gt;怎么做到的？&lt;/p&gt;
&lt;p&gt;不是靠“把 prompt 改得更聪明”，而是靠把该预处理的东西提前做掉。&lt;/p&gt;
&lt;p&gt;现在 &lt;code&gt;blog-style-suite&lt;/code&gt; 的流程，已经很清楚了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;scan&lt;/code&gt; 阶段纯启发式，0 次 AI 调用&lt;/li&gt;
&lt;li&gt;&lt;code&gt;build&lt;/code&gt; 阶段先做启发式评分，还是 0 次 AI 调用&lt;/li&gt;
&lt;li&gt;再按 &lt;code&gt;technical / finance / essay / tooling&lt;/code&gt; 四个 lane 各做 1 次批量精选和打标签&lt;/li&gt;
&lt;li&gt;最后再做 1 次作者风格压缩&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样算下来，冷启动最多也就是 5 次调用。&lt;/p&gt;
&lt;p&gt;更关键的是，这 5 次不是分散在每一篇文章上，而是集中在已经被预处理过的、高价值的摘要材料上。&lt;/p&gt;
&lt;p&gt;这就是预处理真正省 token 的地方。不是少省几个字，而是从“按文章逐篇调用”改成“按阶段集中调用”。&lt;/p&gt;
&lt;p&gt;再往后，缓存也补上了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;builder.py&lt;/code&gt; 里有 lane 的 batch fingerprint，有 provider checkpoint 恢复，有 &lt;code&gt;review_pool_per_lane = 12&lt;/code&gt; 这种为了本地模型上下文做的收缩。你要改一小部分数据，不会整条流水线重跑。&lt;/p&gt;
&lt;p&gt;这类设计看起来都不炫，但每一个都很实用。因为它们解决的都是“别让同一批 token 反复烧第二次”。&lt;/p&gt;
&lt;h2 id=&#34;现在这套数据结构本质上就是在压缩真正有用的信号&#34;&gt;现在这套数据结构，本质上就是在压缩真正有用的信号
&lt;/h2&gt;&lt;p&gt;这一套拆完以后，数据结构也就顺了。&lt;/p&gt;
&lt;p&gt;我现在更愿意把它理解成三层。&lt;/p&gt;
&lt;h3 id=&#34;第一层scanjson&#34;&gt;第一层：&lt;code&gt;scan.json&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;这是共享原料。&lt;/p&gt;
&lt;p&gt;里面放的是文章路径、标题、日期、分类、标签、开头段落、closing stub、headings、screening 结果、lane 归类这些结构化信号。&lt;/p&gt;
&lt;p&gt;它不是给 &lt;code&gt;blog-writer&lt;/code&gt; 直接吃的，它是给生产侧继续往下加工的。&lt;/p&gt;
&lt;h3 id=&#34;第二层providersourcejson&#34;&gt;第二层：&lt;code&gt;{provider}.source.json&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;这是 provider 级 checkpoint。&lt;/p&gt;
&lt;p&gt;在共享原料的基础上，多了评分结果、lane selection、fingerprint、cache status 这些中间态。也就是说，它更像“加工过程中的半成品”，重点是可恢复、可复用、可中断继续。&lt;/p&gt;
&lt;h3 id=&#34;第三层providerruntimejson-和-publishedruntimejson&#34;&gt;第三层：&lt;code&gt;{provider}.runtime.json&lt;/code&gt; 和 &lt;code&gt;published.runtime.json&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;这才是消费侧真正关心的成品。&lt;/p&gt;
&lt;p&gt;里面保留的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;author_style&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lanes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;samples&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;writer_guide&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是把原来一大堆历史文章，压成一份可直接消费的运行时风格资产。&lt;/p&gt;
&lt;p&gt;尤其是 &lt;code&gt;published.runtime.json&lt;/code&gt; 这个发布位很关键。&lt;code&gt;blog-writer&lt;/code&gt; 只读这一份，不回头去扫 &lt;code&gt;content/post&lt;/code&gt;，也不关心 suite 目录下所有 provider 的完整镜像。&lt;/p&gt;
&lt;p&gt;这个边界一旦立住，消费侧就轻了。写稿模型看到的，不再是一堆原始旧文，而是一份已经预处理好的高密度信号。&lt;/p&gt;
&lt;h2 id=&#34;不是所有事情都该让模型做&#34;&gt;不是所有事情都该让模型做
&lt;/h2&gt;&lt;p&gt;我现在越来越认同一个判断：其实不是“多接几个模型”，而是“别把不该让模型做的事也扔给模型”。&lt;/p&gt;
&lt;p&gt;像这些事情，就很适合本地规则先做掉：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;frontmatter 解析&lt;/li&gt;
&lt;li&gt;开头段落提取&lt;/li&gt;
&lt;li&gt;headings 抽取&lt;/li&gt;
&lt;li&gt;本人/转载/模型署名判断&lt;/li&gt;
&lt;li&gt;blockquote 比例检测&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;!--more--&amp;gt;&lt;/code&gt;、嵌入 prompt、正文长度这类硬规则筛掉&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些动作让模型来做，不是不行，是浪费。&lt;/p&gt;
&lt;p&gt;模型更适合做的，是那些带模糊性、带取舍的部分。比如一个 lane 里哪几篇更能代表当前声音，或者从高分文章里提炼作者风格标签。&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;blog-style-suite&lt;/code&gt; 真正值钱的，不只是“省 token”，而是它把人、规则、模型三者各自该干的活重新分了一次。&lt;/p&gt;
&lt;h2 id=&#34;预处理不是为了省一点-token是为了让写稿这件事可持续&#34;&gt;预处理不是为了省一点 token，是为了让写稿这件事可持续
&lt;/h2&gt;&lt;p&gt;第二篇的结论，我想压得更直接一点。&lt;/p&gt;
&lt;p&gt;token 富裕的时候，生吃历史文章当然没问题。甚至你真只写一两篇，可能还更省脑子。&lt;/p&gt;
&lt;p&gt;但只要你想把这件事做成长期工作流，预处理就不是可选项了。因为你不做预处理，写稿模型每次都得重复看旧材料，风格维护和文章生成也永远搅在一起。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;blog-style-suite&lt;/code&gt; 的意义，就是把这一团东西拆开。&lt;/p&gt;
&lt;p&gt;不是为了显得系统，不是为了多一个项目名，而是为了让 &lt;code&gt;blog-writer&lt;/code&gt; 保持轻，保持稳，保持“只干写稿这一个动作”。&lt;/p&gt;
&lt;p&gt;到了这里，下一步的问题也就顺理成章了。&lt;/p&gt;
&lt;p&gt;既然生产侧已经独立出来了，那到底该让什么模型来承担这部分成本？本地模型、在线模型、&lt;code&gt;Minimax&lt;/code&gt;，分别该站在哪个工位上？这件事，留到下一篇 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/how-i-split-local-online-and-minimax-models/&#34; &gt;AI 写博客这件事，后来还是得做成工程（三）：本地模型、在线模型和 Minimax 最后怎么分工&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/84a06b5dc743f2e9bc6e788d53496a1261bc63ae&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;84a06b5dc743f2e9bc6e788d53496a1261bc63ae&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/9e92b8e6a15d03e6392aff7f3b2dcb0992fe5043&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;9e92b8e6a15d03e6392aff7f3b2dcb0992fe5043&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/bc4b950cbb13e37d1fdb16a9d23325cfefa6f90e&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;bc4b950cbb13e37d1fdb16a9d23325cfefa6f90e&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库文件：&lt;code&gt;scripts/blog-style-suite/README.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;仓库文件：&lt;code&gt;scripts/blog-style-suite/style_pipeline/scanner.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;仓库文件：&lt;code&gt;scripts/blog-style-suite/style_pipeline/builder.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;仓库文件：&lt;code&gt;scripts/blog-style-suite/style_pipeline/compressor.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;生效运行时：&lt;code&gt;.agents/data/blog-writing/published.runtime.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 本次的内容比较多，拆分成系列文章：去年就有很多稿子是通过大模型写的，那会是自己写个大纲或者问题清单，然后AI出稿子，复制内容到本地 md 文档，填写头信息，标签信息、发布文章；近期 codex 用了很多，发现 codex 里面的联网搜索能力很强，那我是不是能写个 skill，将这些事情自动化，此时诞生了 skill blog-writer 的第一稿，我还想着让 AI 学习我以前文章的风格，这就导致 blog-writer 运行的时候，很费 token，后续我针对 blog-writer 进行了多个版本的优化，拆分了 数据模块，数据生成的模块，原本数据生成的模块还是独立的 skill，写着写着，我就发现，更适合做成 Python 项目，此时就有了 blog-style-suite，然后我又发现，训练风格数据，也是比较费 token，我就想着用本地的大模型，对接了本地的大模型，我又想到了对比下本地大模型和在线版本的区别，又对接了 minimax；blog-style-suite 和 blog-writer 的演化历史可以分析的 git 提价记录。顺带基于本地 blog-writer、blog-style-suite 的代码，可以讲讲里面的设计思路，是如何做到了节约 token，数据结构是如何设计的，核心的设计思路。Token 富裕完全能生吃历史文章，预处理能节约很多 token&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;这一篇把重点从写稿动作切到数据工程，核心回答“为什么必须拆模块”。&lt;/li&gt;
&lt;li&gt;开头直接承认“生吃历史文章能用”，这样后面的拆分理由才更有说服力。&lt;/li&gt;
&lt;li&gt;重点展开了 &lt;code&gt;scan.json&lt;/code&gt;、&lt;code&gt;source.json&lt;/code&gt;、&lt;code&gt;runtime.json&lt;/code&gt; 这三层结构，避免空讲架构。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bc4b950&lt;/code&gt; 被放在中间当转折点，因为“从约 2000 次到 5 次”最能说明预处理的价值。&lt;/li&gt;
&lt;li&gt;结尾把消费侧和生产侧重新分开，为第三篇的模型分工做铺垫。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>AI 博客工程（一）：blog-writer 为什么会长出来</title>
        <link>https://ttf248.life/p/why-blog-writer-had-to-exist/</link>
        <pubDate>Fri, 03 Apr 2026 20:58:02 +0800</pubDate>
        
        <guid>https://ttf248.life/p/why-blog-writer-had-to-exist/</guid>
        <description>&lt;p&gt;去年写了不少 AI 稿子，那会最土的流程就是，自己先整理个大纲或者问题清单，让大模型把正文吐出来，然后再把内容复制到本地 &lt;code&gt;md&lt;/code&gt; 文档里，补 frontmatter、标签、分类、标题，最后再发布。&lt;/p&gt;
&lt;p&gt;这套流程不是不能用，是很烦。真正费时间的地方，不是正文，而是正文外面那一圈重复劳动。尤其是最近 &lt;code&gt;Codex&lt;/code&gt; 用多了以后，这种别扭感更强了。它能读仓库、能改文件、能补资料、还能直接把文章写进目录里，我要是还手工复制来复制去，反而像是人把工具的腿绑住了。&lt;/p&gt;
&lt;p&gt;这组文章其实就想讲一件事，AI 写博客到了后面，不能再只靠一段 prompt 硬顶。当前这一篇先讲 &lt;code&gt;blog-writer&lt;/code&gt; 为什么会长出来；下一篇会接着写 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/how-blog-style-suite-split-style-and-token-cost/&#34; &gt;AI 写博客这件事，后来还是得做成工程（二）：blog-style-suite 怎么把风格学习和 token 成本拆开&lt;/a&gt;；最后一篇收在 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/how-i-split-local-online-and-minimax-models/&#34; &gt;AI 写博客这件事，后来还是得做成工程（三）：本地模型、在线模型和 Minimax 最后怎么分工&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;真正烦人的不是写稿是那一串机械动作&#34;&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;md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;补 &lt;code&gt;title&lt;/code&gt;、&lt;code&gt;date&lt;/code&gt;、&lt;code&gt;slug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;填标签和分类&lt;/li&gt;
&lt;li&gt;补 &lt;code&gt;&amp;lt;!--more--&amp;gt;&lt;/code&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;p&gt;这也是为什么我后来越来越觉得，&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/command-line-ai-coding-interaction/&#34; &gt;基于命令行的AI编码交互&lt;/a&gt; 这类变化，不只是“换了个入口”。当 AI 已经能直接在仓库里读写文件的时候，博客写作如果还停在“复制正文到本地文档”这一层，整个工作流其实已经落后了。&lt;/p&gt;
&lt;h2 id=&#34;blog-writer-第一层价值不是文风是把契约写死&#34;&gt;blog-writer 第一层价值，不是文风，是把契约写死
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;blog-writer&lt;/code&gt; 最早的那个节点，是 2026 年 4 月 1 日 17:00 的 &lt;code&gt;991536a&lt;/code&gt;。从 git 提交记录看，这一版已经把 &lt;code&gt;SKILL.md&lt;/code&gt;、&lt;code&gt;write_post.py&lt;/code&gt; 和一套最初的风格材料一起放进来了。&lt;/p&gt;
&lt;p&gt;但我后来回头看，这一稿最值钱的地方，不是“AI 学会了我的文风”，而是先把写稿契约写死了。&lt;/p&gt;
&lt;p&gt;什么叫契约写死？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入里至少要有大纲和事实锚点&lt;/li&gt;
&lt;li&gt;输出必须是完整 Markdown，不是半成品&lt;/li&gt;
&lt;li&gt;frontmatter 不能再靠人补&lt;/li&gt;
&lt;li&gt;文章不能只停在聊天窗口里，得直接落到 &lt;code&gt;content/post&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这件事很重要，因为 prompt 本身不稳定。你今天说一句“帮我像以前那样写”，它可能理解成语气像一点；明天你再说一遍，它可能只学到表面句式。可一旦写成 &lt;code&gt;Skill&lt;/code&gt;，规则就从“临场发挥”变成“固定工序”了。&lt;/p&gt;
&lt;p&gt;后面的几个节点，其实都在继续补这个契约。&lt;/p&gt;
&lt;p&gt;2026 年 4 月 2 日 22:54 的 &lt;code&gt;8eb735a&lt;/code&gt;，把作者字段、写作附记、原始提示词这些东西都固定下来了。到这一步，博客成稿已经不是“正文写完就算完”，而是连元信息、可追溯性、公开附记都一起标准化了。&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;blog-writer&lt;/code&gt; 的第一层价值，从来不是让模型显得更会写，而是让写稿这件事终于有了可重复执行的边界。&lt;/p&gt;
&lt;h2 id=&#34;系列模式其实也是写作契约往前走了一步&#34;&gt;系列模式，其实也是写作契约往前走了一步
&lt;/h2&gt;&lt;p&gt;单篇写稳了以后，下一个问题很快就冒出来了。&lt;/p&gt;
&lt;p&gt;有些题目根本不适合挤在一篇里。你如果硬塞，最后往往会变成一篇信息很多、主线很散、每个点都讲不透的长文。&lt;/p&gt;
&lt;p&gt;这也是为什么 2026 年 4 月 2 日 23:55 的 &lt;code&gt;1a5604e&lt;/code&gt; 很关键。那次直接把系列模式和 &lt;code&gt;write_post_series.py&lt;/code&gt; 一起补进来了，文章之间用 &lt;code&gt;relref&lt;/code&gt; 串起来，批量写入时再统一替换。&lt;/p&gt;
&lt;p&gt;这看起来像是个写文件脚本的小升级，实际上不是。&lt;/p&gt;
&lt;p&gt;它说明一件事：写作工程化已经不再只考虑“这一篇怎么生成”，而是开始考虑“这一组内容怎么稳定落盘、怎么保证顺序、怎么在站点里互相跳转”。&lt;/p&gt;
&lt;p&gt;第二天 2026 年 4 月 3 日 09:29 的 &lt;code&gt;04dccb9&lt;/code&gt; 继续把这件事往前推了一步。系列文章时间戳按分钟递增，不再共用一个时间。这个改动很小，但很有工程味，因为它解决的是 Hugo 列表页、上一篇下一篇、系列顺序这些真实问题。&lt;/p&gt;
&lt;p&gt;说白了，系列模式不是为了显得高级，而是为了让“多篇一起发”这件事别再靠手工兜底。&lt;/p&gt;
&lt;h2 id=&#34;但只靠一个-skill后面还是会撞到-token-墙&#34;&gt;但只靠一个 skill，后面还是会撞到 token 墙
&lt;/h2&gt;&lt;p&gt;一旦开始认真折腾文风学习，&lt;code&gt;blog-writer&lt;/code&gt; 的上下文会很快变胖。你不但要让它会写，还想让它像你以前那样写。那最自然的办法，就是把历史文章也一股脑塞进去。&lt;/p&gt;
&lt;p&gt;这么干，单次当然能跑。&lt;/p&gt;
&lt;p&gt;但只要你不是偶尔写一篇，而是想把它变成长期工作流，问题马上就来了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;token 花销高&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;blog-writer&lt;/code&gt; 更适合当消费侧，而不是把所有事情都塞给它做。&lt;/p&gt;
&lt;p&gt;写稿这个动作，应该尽量轻，尽量直接，尽量只读生效版本。至于风格数据怎么生成、怎么筛、怎么压缩，那是另一条生产侧流水线的事。这个判断，最后才把我推到了下一步，也就是 &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/p/how-blog-style-suite-split-style-and-token-cost/&#34; &gt;AI 写博客这件事，后来还是得做成工程（二）：blog-style-suite 怎么把风格学习和 token 成本拆开&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;先把流程做稳后面才有资格谈风格和模型&#34;&gt;先把流程做稳，后面才有资格谈风格和模型
&lt;/h2&gt;&lt;p&gt;现在回头看，&lt;code&gt;blog-writer&lt;/code&gt; 长出来，不是因为我忽然很想做一个博客写作助手。&lt;/p&gt;
&lt;p&gt;更像是因为原来那套流程已经开始不配合新的工作方式了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Codex&lt;/code&gt; 这种工具一旦能联网补资料、能在仓库里读写、能直接调用脚本，写博客这件事就不该再停在“复制正文到本地文档”那一步。你不把这部分自动化，它反而会变成整条链路里最笨的一环。&lt;/p&gt;
&lt;p&gt;所以第一篇的结论我就放这。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;blog-writer&lt;/code&gt; 先解决的，不是文风，而是发布动作的重复劳动。没有这层契约，后面谈 token、谈数据结构、谈本地模型，其实都站不住。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/991536a237d04aba7c44dec501b3d98c644040c8&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;991536a237d04aba7c44dec501b3d98c644040c8&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/8eb735aa8448c97deb2af1ea46b86772008fa9e3&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;8eb735aa8448c97deb2af1ea46b86772008fa9e3&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/1a5604e7e6ce0a13f260fcbb8c2c1d964cdd0892&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;1a5604e7e6ce0a13f260fcbb8c2c1d964cdd0892&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库提交：&lt;a class=&#34;link&#34; href=&#34;https://github.com/ttf248/notebook/commit/04dccb98c55a6ea3b81408012b33a6219cf8ab77&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;04dccb98c55a6ea3b81408012b33a6219cf8ab77&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;仓库文件：&lt;code&gt;.agents/skills/blog-writer/SKILL.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;仓库文件：&lt;code&gt;.agents/skills/blog-writer/scripts/write_post.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;仓库文件：&lt;code&gt;.agents/skills/blog-writer/scripts/write_post_series.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer 本次的内容比较多，拆分成系列文章：去年就有很多稿子是通过大模型写的，那会是自己写个大纲或者问题清单，然后AI出稿子，复制内容到本地 md 文档，填写头信息，标签信息、发布文章；近期 codex 用了很多，发现 codex 里面的联网搜索能力很强，那我是不是能写个 skill，将这些事情自动化，此时诞生了 skill blog-writer 的第一稿，我还想着让 AI 学习我以前文章的风格，这就导致 blog-writer 运行的时候，很费 token，后续我针对 blog-writer 进行了多个版本的优化，拆分了 数据模块，数据生成的模块，原本数据生成的模块还是独立的 skill，写着写着，我就发现，更适合做成 Python 项目，此时就有了 blog-style-suite，然后我又发现，训练风格数据，也是比较费 token，我就想着用本地的大模型，对接了本地的大模型，我又想到了对比下本地大模型和在线版本的区别，又对接了 minimax；blog-style-suite 和 blog-writer 的演化历史可以分析的 git 提价记录。顺带基于本地 blog-writer、blog-style-suite 的代码，可以讲讲里面的设计思路，是如何做到了节约 token，数据结构是如何设计的，核心的设计思路。Token 富裕完全能生吃历史文章，预处理能节约很多 token&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;第一篇先收在工作流触发点，不急着展开 token 和模型分工，避免三篇一起抢主线。&lt;/li&gt;
&lt;li&gt;重点保留了“正文不难，麻烦的是发布前后那串机械动作”这个判断。&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;991536a&lt;/code&gt;、&lt;code&gt;8eb735a&lt;/code&gt;、&lt;code&gt;1a5604e&lt;/code&gt;、&lt;code&gt;04dccb9&lt;/code&gt; 这些节点，把“流程契约化”落到真实 git 演化上。&lt;/li&gt;
&lt;li&gt;系列模式放在这一篇讲，是为了说明博客写作已经从单篇生成走到了整组落盘。&lt;/li&gt;
&lt;li&gt;结尾故意把问题引到 token 墙，为第二篇的数据工程和预处理做铺垫。&lt;/li&gt;
&lt;/ul&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Skill 更像 agent 的工种手册</title>
        <link>https://ttf248.life/p/skill-is-an-agent-handbook/</link>
        <pubDate>Thu, 02 Apr 2026 22:43:16 +0800</pubDate>
        
        <guid>https://ttf248.life/p/skill-is-an-agent-handbook/</guid>
        <description>&lt;p&gt;这几天看 AI 编程，前脚大家还在聊 &lt;code&gt;MCP&lt;/code&gt;，后脚又开始聊 &lt;code&gt;Skill&lt;/code&gt;。这两个词放在一起，最容易让人误会成又多了一个协议，或者又多了一种高级提示词。&lt;/p&gt;
&lt;p&gt;真正的区别其实更朴素：&lt;code&gt;MCP&lt;/code&gt; 解决的是 agent 怎么接上外部工具和数据，&lt;code&gt;Skill&lt;/code&gt; 解决的是接上以后按什么顺序把活干稳。一个偏连接，一个偏流程。&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;Skill&lt;/code&gt; 最有价值的场景，不是模型完全不会做，而是模型每次做法不稳。它像一份工种手册，把触发条件、步骤、边界、参考资料和必要脚本放在一起，让 agent 少靠临场发挥。&lt;/p&gt;
&lt;h2 id=&#34;它先解决做法不稳&#34;&gt;它先解决“做法不稳”
&lt;/h2&gt;&lt;p&gt;如果用最通俗的话来解释，我会这么说：你把一个老员工脑子里的熟练做法，整理成一份能复用、能触发、能落地的说明书，这东西就是 &lt;code&gt;Skill&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;OpenAI 官方文档的定义也很直接，&lt;code&gt;Skill&lt;/code&gt; 是一类面向特定任务的能力包，里面可以放指令、参考资料，以及可选的脚本。目的不是让模型“更聪明”，而是让它在某一类任务上，按固定工作流稳定输出。&lt;/p&gt;
&lt;p&gt;它不像普通 prompt，因为它不是一次性说两句就完了；也不像 &lt;code&gt;MCP&lt;/code&gt;，因为它不负责连工具和数据源；更不像 &lt;code&gt;AGENTS.md&lt;/code&gt;，因为它不是全仓库通用规矩。它更像专项 SOP，或者说工种手册。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;处理 GitHub PR 评论时，先看哪些评论要处理，再问用户处理哪几条，再改代码，再反馈结果&lt;/li&gt;
&lt;li&gt;排查 CI 失败时，先拉取 GitHub Actions 日志，再提炼失败片段，再给修复计划，批准后才动手&lt;/li&gt;
&lt;li&gt;写博客时，先按固定文风生成标题，再补事实，再补 frontmatter，再落盘&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些事的共同点都不是“模型不知道怎么回答”，而是“模型每次做法都不一样，很容易跑偏”。这时候，&lt;code&gt;Skill&lt;/code&gt; 的价值就出来了：把会反复发生的偏差，提前写进流程里。&lt;/p&gt;
&lt;h2 id=&#34;mcp-接世界skill-定流程&#34;&gt;MCP 接世界，Skill 定流程
&lt;/h2&gt;&lt;p&gt;这事还是得放回真实工作流里看，不然很容易讲飘。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;MCP&lt;/code&gt; 像接口层。&lt;/p&gt;
&lt;p&gt;官方对 &lt;code&gt;MCP&lt;/code&gt; 的定义，是把 AI 应用连接到外部系统的开放标准。文件、本地数据库、搜索引擎、设计稿、第三方服务，这些都可以通过 &lt;code&gt;MCP&lt;/code&gt; 接进来。所以它解决的是“接进去”。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Skill&lt;/code&gt; 像流程层。&lt;/p&gt;
&lt;p&gt;OpenAI 的 &lt;code&gt;Agent Skills&lt;/code&gt; 文档里写得很清楚，&lt;code&gt;Skill&lt;/code&gt; 是可复用工作流的编写格式。一个 &lt;code&gt;Skill&lt;/code&gt; 至少要有一个 &lt;code&gt;SKILL.md&lt;/code&gt;，还可以带 &lt;code&gt;scripts/&lt;/code&gt;、&lt;code&gt;references/&lt;/code&gt;、&lt;code&gt;assets/&lt;/code&gt;。Codex 会先读取它的 &lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;description&lt;/code&gt;，只有在判断要用的时候，才把完整说明载入上下文。这就是官方说的 &lt;code&gt;progressive disclosure&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;所以这两者的分工很清楚：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MCP&lt;/code&gt;：把能力接进来&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Skill&lt;/code&gt;：把做事顺序定下来&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;拿一个很直观的例子来说，Figma 转代码这种事，如果 agent 根本读不到设计稿，那你先补的是 &lt;code&gt;MCP&lt;/code&gt;。但如果它已经能读设计稿了，只是每次都乱写，今天先写组件，明天先切页面，后天又忘了做视觉检查，那你补的就该是 &lt;code&gt;Skill&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;开发-skill先找重复偏差&#34;&gt;开发 Skill，先找重复偏差
&lt;/h2&gt;&lt;p&gt;这东西真没想象中那么重。&lt;/p&gt;
&lt;p&gt;OpenAI 官方建议先用内置的 &lt;code&gt;$skill-creator&lt;/code&gt;，让它帮你把触发条件、范围、是否需要脚本这些骨架先搭出来。默认优先是 instruction-only，也就是先别急着上脚本，先把说明写清楚。&lt;/p&gt;
&lt;p&gt;如果手工写，最小结构也很简单：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;my-skill/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── SKILL.md
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── scripts/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── references/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── assets/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中真正必需的，只有 &lt;code&gt;SKILL.md&lt;/code&gt;。并且这个文件至少要有两项元数据：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nn&#34;&gt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;skill-name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Explain exactly when this skill should and should not trigger.&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nn&#34;&gt;---&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;真正要避免的，是把 Skill 写成“我希望模型更聪明”的愿望清单。它应该回答的是更具体的问题：哪类任务会反复出现，哪一步总跑偏，哪些资料只有在这类任务里才需要加载，哪些动作必须用脚本保证确定性。&lt;/p&gt;
&lt;h3 id=&#34;1-先找重复出现的偏差&#34;&gt;1. 先找“重复出现的偏差”
&lt;/h3&gt;&lt;p&gt;不是所有任务都值得做成 &lt;code&gt;Skill&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果你只是偶尔让 agent 干一次活，普通 prompt 就够了。真正适合抽成 &lt;code&gt;Skill&lt;/code&gt; 的，通常是这种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你已经反复说过很多次同样的话&lt;/li&gt;
&lt;li&gt;agent 明明有能力，但执行顺序总变&lt;/li&gt;
&lt;li&gt;每次出错的位置都差不多&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;说白了，不是“能力不够”，而是“套路不稳”。&lt;/p&gt;
&lt;h3 id=&#34;2-把-description-写成触发条件&#34;&gt;2. 把 &lt;code&gt;description&lt;/code&gt; 写成触发条件
&lt;/h3&gt;&lt;p&gt;这一步非常关键。&lt;/p&gt;
&lt;p&gt;官方文档专门强调，Codex 是否会隐式调用一个 &lt;code&gt;Skill&lt;/code&gt;，很依赖 &lt;code&gt;description&lt;/code&gt;。所以别写“这是一个很好用的技能”，要写“什么时候该用，什么时候不该用”。&lt;/p&gt;
&lt;p&gt;比如 &lt;code&gt;gh-fix-ci&lt;/code&gt; 官方技能的描述就很清楚：当用户要你调试或修复 GitHub PR 检查失败时使用；重点是检查日志、总结失败原因、给出修复计划，并且在得到明确批准后再实施。你一看就知道它的边界在哪里。&lt;/p&gt;
&lt;h3 id=&#34;3-能靠说明搞定就先别写脚本&#34;&gt;3. 能靠说明搞定，就先别写脚本
&lt;/h3&gt;&lt;p&gt;OpenAI 文档的建议也很务实，除非你明确需要确定性行为或者外部工具，不然优先用说明，不要一上来就脚本化。&lt;/p&gt;
&lt;p&gt;为什么？因为脚本一多，维护成本就上来了。&lt;/p&gt;
&lt;p&gt;很多 &lt;code&gt;Skill&lt;/code&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;p&gt;只有当某一步特别稳定、特别机械、特别适合自动化时，再把它下沉到 &lt;code&gt;scripts/&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;4-把资料模版和资源拆出去&#34;&gt;4. 把资料、模版和资源拆出去
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Skill&lt;/code&gt; 写着写着，很容易变成一坨超长说明文。这时候就该拆。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SKILL.md&lt;/code&gt; 负责讲规则和顺序&lt;/li&gt;
&lt;li&gt;&lt;code&gt;references/&lt;/code&gt; 放背景文档和查阅材料&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assets/&lt;/code&gt; 放模版、图标、样例&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scripts/&lt;/code&gt; 放能稳定执行的动作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样做有两个好处。&lt;/p&gt;
&lt;p&gt;第一，主文件不会越来越臃肿。第二，模型只有在需要时才加载细节，符合 &lt;code&gt;progressive disclosure&lt;/code&gt; 这套思路。&lt;/p&gt;
&lt;h3 id=&#34;5-本地先用跨项目再分发&#34;&gt;5. 本地先用，跨项目再分发
&lt;/h3&gt;&lt;p&gt;官方文档把这件事也分得很清楚。&lt;/p&gt;
&lt;p&gt;如果你只是给自己当前仓库用，放在 &lt;code&gt;.agents/skills/&lt;/code&gt; 就够了。Codex 会从仓库、用户、系统这些位置扫描技能目录。&lt;/p&gt;
&lt;p&gt;如果你已经发现这套东西不只一个仓库能用，或者想把多个技能一起打包分发，那就别只停在 skill folder 这一层了，应该考虑 &lt;code&gt;plugin&lt;/code&gt;。OpenAI 官方文档的原话也很明确，&lt;code&gt;Skill&lt;/code&gt; 是工作流本身，&lt;code&gt;plugin&lt;/code&gt; 才是更适合安装和分发的单位。&lt;/p&gt;
&lt;h2 id=&#34;哪些场景适合&#34;&gt;哪些场景适合
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Skill&lt;/code&gt; 不是越多越好，它最适合的是下面几类活。&lt;/p&gt;
&lt;h3 id=&#34;高频重复任务&#34;&gt;高频重复任务
&lt;/h3&gt;&lt;p&gt;你每周都会做，而且每次顺序都差不多。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;处理 PR review 评论&lt;/li&gt;
&lt;li&gt;写博客并补 frontmatter&lt;/li&gt;
&lt;li&gt;排查 CI 失败&lt;/li&gt;
&lt;li&gt;做发布前检查&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这类活，最怕的不是模型不会，而是每次都要重新讲一遍。&lt;/p&gt;
&lt;h3 id=&#34;有固定流程的任务&#34;&gt;有固定流程的任务
&lt;/h3&gt;&lt;p&gt;某件事就是天然有先后顺序。&lt;/p&gt;
&lt;p&gt;比如排查问题，你就应该先看日志，再收敛范围，再提计划，再改。这个时候，&lt;code&gt;Skill&lt;/code&gt; 特别合适，因为它能把顺序写死，减少模型临场发挥。&lt;/p&gt;
&lt;h3 id=&#34;需要绑定专业语境的任务&#34;&gt;需要绑定专业语境的任务
&lt;/h3&gt;&lt;p&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;这类活，如果你每次都只靠 prompt 临时说，很容易漏。&lt;/p&gt;
&lt;h3 id=&#34;工具和流程要一起用的任务&#34;&gt;工具和流程要一起用的任务
&lt;/h3&gt;&lt;p&gt;这个场景特别典型。&lt;/p&gt;
&lt;p&gt;不是单纯“连上 GitHub”就完了，而是连上以后，还得按某种方法去看日志、归纳问题、决定要不要修改、最后怎么反馈。也就是说，外部连接和内部流程必须一起工作。&lt;/p&gt;
&lt;p&gt;这时候常常就是 &lt;code&gt;MCP + Skill&lt;/code&gt; 联合出场。&lt;/p&gt;
&lt;h2 id=&#34;哪些场景不必上&#34;&gt;哪些场景不必上
&lt;/h2&gt;&lt;p&gt;反过来也要说清楚，不然很容易什么都想做成 &lt;code&gt;Skill&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;一次性的随口任务&#34;&gt;一次性的随口任务
&lt;/h3&gt;&lt;p&gt;用户临时问一句，普通 prompt 往往就够了。&lt;/p&gt;
&lt;h3 id=&#34;你只是想接一个外部系统&#34;&gt;你只是想“接一个外部系统”
&lt;/h3&gt;&lt;p&gt;那优先考虑 &lt;code&gt;MCP&lt;/code&gt;，不是 &lt;code&gt;Skill&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;你想约束整个仓库的长期行为&#34;&gt;你想约束整个仓库的长期行为
&lt;/h3&gt;&lt;p&gt;这更像 &lt;code&gt;AGENTS.md&lt;/code&gt; 的活，不是 &lt;code&gt;Skill&lt;/code&gt; 的活。&lt;/p&gt;
&lt;p&gt;所以简单总结一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;缺连接，用 &lt;code&gt;MCP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;缺流程，用 &lt;code&gt;Skill&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;缺全局规矩，用 &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;看几个案例就够了&#34;&gt;看几个案例就够了
&lt;/h2&gt;&lt;p&gt;概念讲再多，不如看几个真的案例。&lt;/p&gt;
&lt;h3 id=&#34;1-roll-dice最小可用的入门案例&#34;&gt;1. &lt;code&gt;roll-dice&lt;/code&gt;：最小可用的入门案例
&lt;/h3&gt;&lt;p&gt;这个案例来自 OpenAI 官方 &lt;code&gt;Agent Skills&lt;/code&gt; 文档。&lt;/p&gt;
&lt;p&gt;它非常小，目录里几乎只有一个 &lt;code&gt;SKILL.md&lt;/code&gt;，让 agent 在用户要求掷骰子时，调用 PowerShell 的随机数命令。&lt;/p&gt;
&lt;p&gt;为什么这个例子好？&lt;/p&gt;
&lt;p&gt;因为它把 &lt;code&gt;Skill&lt;/code&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;/ul&gt;
&lt;p&gt;它说明了一件事，&lt;code&gt;Skill&lt;/code&gt; 不一定非得很大。只要某件事会重复发生，而且你不希望模型瞎发挥，就可以做成 &lt;code&gt;Skill&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;2-gh-address-comments处理-github-评论的流程案例&#34;&gt;2. &lt;code&gt;gh-address-comments&lt;/code&gt;：处理 GitHub 评论的流程案例
&lt;/h3&gt;&lt;p&gt;这个案例来自 OpenAI 官方的 &lt;a class=&#34;link&#34; href=&#34;https://github.com/openai/skills&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;openai/skills&lt;/code&gt;&lt;/a&gt; 仓库。&lt;/p&gt;
&lt;p&gt;它的目标不是“连 GitHub”，而是把“处理当前分支 PR 的评论”这件事固化下来。官方版本里的步骤就很典型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先确认 &lt;code&gt;gh&lt;/code&gt; 是否已经认证&lt;/li&gt;
&lt;li&gt;再拉出当前 PR 的评论和 review threads&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;Skill&lt;/code&gt; 的价值。&lt;/p&gt;
&lt;p&gt;很多工程任务，难点根本不在“模型知不知道 GitHub 是什么”，而在“它会不会按对的顺序处理事情”。&lt;code&gt;gh-address-comments&lt;/code&gt; 解决的，就是这类顺序问题。&lt;/p&gt;
&lt;h3 id=&#34;3-gh-fix-ci排查-ci-失败的工程案例&#34;&gt;3. &lt;code&gt;gh-fix-ci&lt;/code&gt;：排查 CI 失败的工程案例
&lt;/h3&gt;&lt;p&gt;这也是 &lt;code&gt;openai/skills&lt;/code&gt; 仓库里的官方技能。&lt;/p&gt;
&lt;p&gt;它针对的是另一种很典型的工程任务：PR 检查挂了，要不要修，怎么修。&lt;/p&gt;
&lt;p&gt;这个 &lt;code&gt;Skill&lt;/code&gt; 里定义的流程也很有代表性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先确认 &lt;code&gt;gh&lt;/code&gt; 登录状态&lt;/li&gt;
&lt;li&gt;找到当前 PR&lt;/li&gt;
&lt;li&gt;拉 GitHub Actions 的失败检查和日志&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;这就不是“帮我看看 CI 为什么挂了”一句 prompt 能稳定搞定的场景了。因为它里面有权限、日志、外部工具、审批边界、执行顺序，这些都需要定下来。&lt;/p&gt;
&lt;h3 id=&#34;4-仓库私有-skill把团队自己的套路沉淀下来&#34;&gt;4. 仓库私有 Skill：把团队自己的套路沉淀下来
&lt;/h3&gt;&lt;p&gt;除了官方案例，我觉得 &lt;code&gt;Skill&lt;/code&gt; 更大的价值，其实是在私有仓库里。&lt;/p&gt;
&lt;p&gt;比如眼前这个博客仓库里的 &lt;code&gt;blog-writer&lt;/code&gt;，本质上就是一个很典型的 repo-scoped skill。它不负责“让模型学会写中文”，而是把这个仓库已经固定下来的文风、结构、事实核验、输出路径、落盘格式都写成工作流。&lt;/p&gt;
&lt;p&gt;这类 &lt;code&gt;Skill&lt;/code&gt; 往往最有现实价值。因为它不是面向所有人，而是专门解决“你这个仓库里，哪类活总在重复出现，而且总容易跑偏”。&lt;/p&gt;
&lt;h2 id=&#34;什么时候该认真做&#34;&gt;什么时候该认真做
&lt;/h2&gt;&lt;p&gt;所以最后还是回到最实际的问题，什么时候你应该认真做一个 &lt;code&gt;Skill&lt;/code&gt;？&lt;/p&gt;
&lt;p&gt;我的答案是：&lt;/p&gt;
&lt;p&gt;当你发现问题已经不再是“模型能力不够”，而是“它每次做法都不稳”的时候。&lt;/p&gt;
&lt;p&gt;这时候继续堆 prompt，收益通常越来越低。你今天补一句，明天再补一句，最后 prompt 长得像流水账，模型还是会漏。&lt;/p&gt;
&lt;p&gt;反而是把它整理成 &lt;code&gt;Skill&lt;/code&gt;，把触发条件、步骤、边界、脚本、资料分开管理，效果更稳，也更能复用。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;MCP&lt;/code&gt; 把外部世界接进来，&lt;code&gt;Skill&lt;/code&gt; 把内部套路固化下来。&lt;/p&gt;
&lt;p&gt;前者解决“能不能做”，后者解决“怎么稳定地做”。&lt;/p&gt;
&lt;p&gt;这就是我现在理解的 &lt;code&gt;Skill&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;它不是新提示词，也不是新协议。它更像是给 agent 配工种手册：少一点临场发挥，多一点可复用的做事顺序。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.openai.com/codex/skills&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Agent Skills - Codex | OpenAI Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://developers.openai.com/blog/skills-agents-sdk&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Using skills to accelerate OSS maintenance | OpenAI Developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://modelcontextprotocol.io/docs/getting-started/intro&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;What is the Model Context Protocol (MCP)? | Model Context Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/openai/skills&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;openai/skills | GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/openai/skills/blob/main/skills/.curated/gh-address-comments/SKILL.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gh-address-comments/SKILL.md | openai/skills&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/openai/skills/blob/main/skills/.curated/gh-fix-ci/SKILL.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;gh-fix-ci/SKILL.md | openai/skills&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;提示词：AI 大模型编程，先是出现了 MCP，然后是 Skill，用通俗的写法，解释下 Skill 是什么，如何开发 Skill，什么场景适用 Skill，分别出处具体的，有代表性的案例&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;p&gt;这次重写保留了原稿里的官方定义、&lt;code&gt;MCP&lt;/code&gt; 边界、开发结构和官方案例，但把文章主线从“概念综述”改成“Skill 解决做法不稳”。压缩了定义式段落，强化了触发条件、重复偏差和流程边界。&lt;/p&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>低成本模型该接的活：重复、脏活和边界清楚的任务</title>
        <link>https://ttf248.life/p/weaker-models-shouldnt-do-frontier-work/</link>
        <pubDate>Thu, 02 Apr 2026 22:05:00 +0800</pubDate>
        
        <guid>https://ttf248.life/p/weaker-models-shouldnt-do-frontier-work/</guid>
        <description>&lt;p&gt;最近把一些边角活往 &lt;code&gt;MiniMax&lt;/code&gt; 和本地模型上迁，感受反而比一开始更清楚：很多任务不是非要最强模型才能做，而是不能把所有模型都放到同一个工位上比。&lt;/p&gt;
&lt;p&gt;以前我容易问“这个模型强不强”。现在更愿意先问两件事：这件活错了以后代价有多大，重复跑一百次以后成本有多高。答案一变，低成本模型、本地模型和旗舰模型的位置也就变了。&lt;/p&gt;
&lt;p&gt;复杂编码、长链路推理、模糊需求拆解，还是该给更强的模型；数据清洗、文档改写、资料初筛、固定格式转换，反而更适合交给便宜模型和本地模型。它们不一定聪明到能总包项目，但很适合把那些不值得人反复动手的活接住。&lt;/p&gt;
&lt;h2 id=&#34;先分风险再分模型&#34;&gt;先分风险，再分模型
&lt;/h2&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;p&gt;更多的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把一堆脏字段洗干净&lt;/li&gt;
&lt;li&gt;把零散资料整理成可读文档&lt;/li&gt;
&lt;li&gt;把长文改成摘要、FAQ、提纲&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;ul&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=&#34;minimax-适合先接边角工序&#34;&gt;MiniMax 适合先接边角工序
&lt;/h2&gt;&lt;p&gt;先说 &lt;code&gt;MiniMax&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;官方对 &lt;code&gt;MiniMax-M2.5&lt;/code&gt; 的定位其实很高，新闻稿和开放平台文档里都把它往编程、工具调用、搜索、办公生产力这些场景上推，甚至强调速度和价格优势。这些说法我不是完全不信，但我更愿意把它拆开看。&lt;/p&gt;
&lt;p&gt;在我这里，&lt;code&gt;MiniMax&lt;/code&gt; 真正顺手的，不是“最复杂的开发任务”，而是下面这些：&lt;/p&gt;
&lt;h3 id=&#34;数据清洗&#34;&gt;数据清洗
&lt;/h3&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;li&gt;表格字段补全&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这类工作最怕的不是模型“笨”，而是格式不稳、输出发散。只要模型能比较老实地按 &lt;code&gt;JSON&lt;/code&gt;、表格、固定模版吐结果，其实就够用了。强模型当然也能做，但拿最贵那档模型去洗字段，很多时候不划算。&lt;/p&gt;
&lt;h3 id=&#34;文档编写&#34;&gt;文档编写
&lt;/h3&gt;&lt;p&gt;文档这活很烦，不是难，是烦。&lt;/p&gt;
&lt;p&gt;接口变了，流程变了，字段改了，说明文档就得跟着改。这个过程其实不太需要模型有多强的创造力，反而更需要它别乱发挥，别把原本明确的东西改得似是而非。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;MiniMax&lt;/code&gt; 做这类事，经常比想象中靠谱。尤其是当你已经把上下文准备好了，它更像一个能干活的文档助理，而不是一个真正的工程师。&lt;/p&gt;
&lt;h3 id=&#34;方案资料搜索&#34;&gt;方案资料搜索
&lt;/h3&gt;&lt;p&gt;官方自己也在推搜索和工具调用，这方向没问题。&lt;/p&gt;
&lt;p&gt;很多时候我们不是要模型“凭空想出答案”，而是要它把网页、文档、公告、资料先找回来，再顺手理一遍。这个场景里，&lt;code&gt;MiniMax&lt;/code&gt; 这类便宜模型的价值就很明显了，因为搜索、摘要、整合，本来就是高频杂活。&lt;/p&gt;
&lt;p&gt;所以我的实际看法是：&lt;code&gt;MiniMax&lt;/code&gt; 不是“不行”，而是更适合先接生产链路里的脏活、累活、重复活。你让它打杂，它常常是合格的；你让它总包整个工程，失望概率就会上来。&lt;/p&gt;
&lt;h2 id=&#34;本地模型的价值是把批处理搬回来&#34;&gt;本地模型的价值，是把批处理搬回来
&lt;/h2&gt;&lt;p&gt;再往下看，本地部署其实是同一个逻辑。&lt;/p&gt;
&lt;p&gt;很多人一说本地模型，就会忍不住想一个问题：能不能替代云端旗舰？&lt;/p&gt;
&lt;p&gt;我觉得这个问题一开始就问偏了。&lt;/p&gt;
&lt;p&gt;本地 &lt;code&gt;12B&lt;/code&gt; 左右模型的现实价值，不是证明自己也能做旗舰模型那档任务，而是把稳定、重复、敏感、低利润但高频的活搬回来。&lt;/p&gt;
&lt;h3 id=&#34;翻译&#34;&gt;翻译
&lt;/h3&gt;&lt;p&gt;这是本地模型最顺手的场景之一。&lt;/p&gt;
&lt;p&gt;像 &lt;code&gt;Qwen2.5&lt;/code&gt; 官方博客里就明确提到，它对长文本生成、结构化数据理解、&lt;code&gt;JSON&lt;/code&gt; 输出都有增强，而且支持超过 &lt;code&gt;29&lt;/code&gt; 种语言。这个组合天生就适合翻译、双语改写、格式统一、术语规范化这类工作。&lt;/p&gt;
&lt;p&gt;技术文档、字段说明、产品介绍、接口注释，这些东西往往结构稳定、术语固定，本地模型不一定翻得最优雅，但通常够用。&lt;/p&gt;
&lt;h3 id=&#34;数据清洗-1&#34;&gt;数据清洗
&lt;/h3&gt;&lt;p&gt;这也是本地模型特别有现实感的地方。&lt;/p&gt;
&lt;p&gt;很多表格、文档、业务资料，你未必想扔到云端。尤其是内部数据、客户资料、会议纪要、半成品方案，这些东西一旦涉及隐私和权限，本地跑就会让人安心很多。&lt;/p&gt;
&lt;p&gt;这时候本地 &lt;code&gt;12B&lt;/code&gt; 左右模型的意义，不是“它有多聪明”，而是“它就在我机器上，而且能稳定干完这类脏活”。&lt;/p&gt;
&lt;h3 id=&#34;固定格式改写&#34;&gt;固定格式改写
&lt;/h3&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;bug 描述改写成工单格式&lt;/li&gt;
&lt;li&gt;中英混杂文本清成单语版本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这类任务的特点都很一致：规则清晰，批量大，重复高，单次价值不高，但总量很烦。&lt;/p&gt;
&lt;p&gt;这正是本地模型最该干的活。&lt;/p&gt;
&lt;h2 id=&#34;3060-12gb-到底能不能带-12b-左右的模型&#34;&gt;3060 12GB 到底能不能带 12B 左右的模型
&lt;/h2&gt;&lt;p&gt;这件事我更愿意写得现实一点：&lt;code&gt;能带，但别想得太美。&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Google 在 &lt;code&gt;Gemma 3&lt;/code&gt; 官方文档里给过一张很有参考价值的显存表。&lt;code&gt;Gemma 3 12B&lt;/code&gt; 大致需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;20 GB&lt;/code&gt; 左右显存加载全精度版本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;12.2 GB&lt;/code&gt; 左右加载中等量化版本&lt;/li&gt;
&lt;li&gt;&lt;code&gt;8.7 GB&lt;/code&gt; 左右加载更低显存占用版本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;官方也专门提醒，这只是模型加载占用，不包含提示词和运行时额外开销。&lt;/p&gt;
&lt;p&gt;这句话很关键。&lt;/p&gt;
&lt;p&gt;它意味着什么？&lt;/p&gt;
&lt;p&gt;意味着像 &lt;code&gt;3060 12GB&lt;/code&gt; 这种卡，跑 &lt;code&gt;12B&lt;/code&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;12B&lt;/code&gt; 确实是能跑起来的。至少做翻译、摘要、表格清洗、固定格式转换，这类任务并不夸张。&lt;/p&gt;
&lt;p&gt;另外，&lt;code&gt;Qwen2.5-14B-Instruct-GGUF&lt;/code&gt; 的官方仓库本身也提供了多种量化格式，这其实已经把思路说得很清楚了：这一档模型，本来就是面向本地推理生态在做适配。&lt;/p&gt;
&lt;p&gt;所以我的结论一直都不是“3060 12GB 能轻松驾驭 12B 模型”，而是：&lt;/p&gt;
&lt;p&gt;它能把这类模型带起来，但更适合跑低预期、高重复、重隐私的工作。这个边界讲清楚，比单纯说“3060 能不能带 12B”更有用。&lt;/p&gt;
&lt;h2 id=&#34;便宜模型和本地模型省下来的不只是-api-钱&#34;&gt;便宜模型和本地模型，省下来的不只是 API 钱
&lt;/h2&gt;&lt;p&gt;很多人聊这件事，第一反应总是省钱。&lt;/p&gt;
&lt;p&gt;当然，省钱很重要。但我觉得更大的价值，其实是你开始敢把以前懒得做的一堆边角活交出去。&lt;/p&gt;
&lt;p&gt;以前你可能不会为了几百条数据清洗，专门写一轮脚本。也不会为了几十页中英文资料统一格式，手工一点点改。更不会为了临时出一份方案搜集材料，把网页挨个读完再整理。&lt;/p&gt;
&lt;p&gt;现在不一样了。&lt;/p&gt;
&lt;p&gt;只要成本足够低，门槛足够低，这些原本“不值得动手”的活，就一下子变得值得了。你不再纠结“要不要做”，而是直接扔给便宜模型或者本地模型先跑一遍。&lt;/p&gt;
&lt;p&gt;这才是我眼里最现实的变化。&lt;/p&gt;
&lt;p&gt;强模型负责攻坚，低成本模型负责打杂，本地模型负责兜底和批处理。&lt;/p&gt;
&lt;p&gt;这么分工，整个工作流才顺。&lt;/p&gt;
&lt;h2 id=&#34;结尾&#34;&gt;结尾
&lt;/h2&gt;&lt;p&gt;所以最后不是“弱模型别用”，而是别让一个模型包打天下。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;MiniMax&lt;/code&gt; 这类模型，不适合被拿去硬刚复杂工程、模糊需求和多轮推理；但你拿它去做数据清洗、文档编写、方案资料搜索，反而常常挺顺手。&lt;/p&gt;
&lt;p&gt;本地 &lt;code&gt;12B&lt;/code&gt; 左右的模型也一样。它们不是为了证明“我已经不需要云端旗舰了”，而是为了把一些稳定、重复、敏感、批量大的活，老老实实搬回自己机器上。&lt;/p&gt;
&lt;p&gt;说白了，低成本模型最值钱的地方，不是替你证明它也能做强模型的活，而是让一堆原本懒得做、舍不得做、反复做的边角任务终于有人接。&lt;/p&gt;
&lt;p&gt;工位放对，它们就有现实价值。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.minimax.io/news/minimax-m25&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;MiniMax M2.5: Built for Real-World Productivity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://platform.minimaxi.com/docs/guides/text-generation&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;MiniMax 开放平台：文本生成&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://qwenlm.github.io/blog/qwen2.5/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Qwen2.5: A Party of Foundation Models!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://huggingface.co/Qwen/Qwen2.5-14B-Instruct-GGUF&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Qwen2.5-14B-Instruct-GGUF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ai.google.dev/gemma/docs/core&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Gemma 3 model overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;minimax 的大模型，能力弱是弱，做一些数据清洗的工作、文档编写、方案资料搜索还是没问题的; 同样的逻辑，本地部署大模型，做一些翻译类的工作，数据清洗的工作也是不错的，模型的参数量在 12b 左右，本地 3060 12GB 的显卡也能带得动&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;p&gt;这次重写保留了原稿里的任务分工、&lt;code&gt;MiniMax&lt;/code&gt;、本地模型和 &lt;code&gt;3060 12GB&lt;/code&gt; 显存边界，但把“弱模型”这种粗标签改成“低成本模型/本地模型该放在哪个工位”。压掉的是口号式结论和重复解释，保留的是按风险、成本和隐私来分工的判断。&lt;/p&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>低价 API 中转站的终局：三月份的大模型体验与不可能三角</title>
        <link>https://ttf248.life/p/the-end-of-low-cost-api-relays/</link>
        <pubDate>Mon, 30 Mar 2026 20:00:00 +0800</pubDate>
        
        <guid>https://ttf248.life/p/the-end-of-low-cost-api-relays/</guid>
        <description>&lt;p&gt;整个三月份，我都在不同的大模型 API 中转站之间来回试。&lt;/p&gt;
&lt;p&gt;便宜，确实是便宜。一个月花不了多少钱，就能把 ChatGPT、Claude、Gemini 之类的国外模型都摸一遍，表面上看，像是找到了一个性价比极高的解法。但真正用下来以后，我越来越觉得，这条路从一开始就绕不开一个不可能三角：&lt;strong&gt;质量、稳定、划算，三者很难同时成立。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;到了上周末，这件事基本也算彻底明牌了。&lt;code&gt;2026-03-28&lt;/code&gt; 到 &lt;code&gt;2026-03-29&lt;/code&gt; 这两天，ChatGPT 相关通道的风控体感明显收紧，Claude 这边也一样，很多原来还能凑合用的低价中转，突然就变得不稳定，甚至直接失效。对我来说，这基本宣告了低价 API 中转模式的阶段性终局。&lt;/p&gt;
&lt;h2 id=&#34;什么是中转站&#34;&gt;什么是中转站
&lt;/h2&gt;&lt;p&gt;先把概念说清楚。&lt;/p&gt;
&lt;p&gt;所谓 &lt;strong&gt;API 中转站&lt;/strong&gt;，本质上并不是模型厂商官方提供的服务，而是夹在用户和上游模型之间的一层“转发层”。你把请求发给中转站，中转站再替你把请求转发给 OpenAI、Anthropic 或其他模型服务商，最后再把结果回传给你。&lt;/p&gt;
&lt;p&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;/p&gt;
&lt;h2 id=&#34;他们一般是怎么工作的&#34;&gt;他们一般是怎么工作的
&lt;/h2&gt;&lt;p&gt;不同平台做法不完全一样，但常见模式大致就那么几类。&lt;/p&gt;
&lt;h3 id=&#34;1-key-二次分发&#34;&gt;1. Key 二次分发
&lt;/h3&gt;&lt;p&gt;有些平台本质上是拿自己的上游 API Key 做统一转发，再把额度切分给下游用户。你买的是它的一层套餐，不是 OpenAI 或 Anthropic 官方直接卖给你的套餐。&lt;/p&gt;
&lt;p&gt;这种模式的问题在于，只要上游 Key 被限制、额度被打满、策略被调整，下游体验就会立刻跟着抖。&lt;/p&gt;
&lt;h3 id=&#34;2-账号池轮换&#34;&gt;2. 账号池轮换
&lt;/h3&gt;&lt;p&gt;圈子里常说的“&lt;strong&gt;号池&lt;/strong&gt;”，可以理解成一批账号资源的池化管理。平台把多个账号集中起来，按请求轮换调用，用来分摊配额压力和风控压力。&lt;/p&gt;
&lt;p&gt;这里的“号池”并不是模型厂商的官方术语，而是一个更偏民间、黑话化的描述。它强调的不是产品能力，而是资源调度方式。谁的池子大，谁短期看起来就更稳；但只要上游开始集中清理，这种稳定往往会瞬间消失。&lt;/p&gt;
&lt;h3 id=&#34;3-逆向封装&#34;&gt;3. 逆向封装
&lt;/h3&gt;&lt;p&gt;还有一类做法，本质上不是走公开、标准的官方 API，而是去研究网页端、客户端的请求方式，再把这些调用过程重新封装成“像 API 一样”的接口给用户用。&lt;/p&gt;
&lt;p&gt;这种“&lt;strong&gt;逆向&lt;/strong&gt;”说白了，就是不是沿着官方给你的门进，而是从侧门、窗口甚至管道去理解系统怎么通信，再自己包一层壳。&lt;/p&gt;
&lt;p&gt;这种方式的短板也很明显：今天能用，不代表明天还能用。页面结构、鉴权方式、设备校验、行为策略一变，整个链路就可能失效。&lt;/p&gt;
&lt;h2 id=&#34;三月份的实际体验&#34;&gt;三月份的实际体验
&lt;/h2&gt;&lt;p&gt;三月份这轮密集体验下来，我最大的感受不是“便宜真香”，而是你必须持续忍受不确定性。&lt;/p&gt;
&lt;p&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;li&gt;同样花时间排查问题，隐性成本并不低&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，低价中转最大的问题，不是它“不便宜”，而是它把很多原本应该由官方平台承担的确定性，重新转嫁给了用户自己。&lt;/p&gt;
&lt;p&gt;你表面上省的是钱，实际上多花的是时间、心力和工作流的可预期性。&lt;/p&gt;
&lt;h2 id=&#34;为什么我说它走进了不可能三角&#34;&gt;为什么我说它走进了不可能三角
&lt;/h2&gt;&lt;p&gt;这段时间反复用下来，我越来越觉得，这类中转服务注定会掉进一个不可能三角。&lt;/p&gt;
&lt;h3 id=&#34;质量&#34;&gt;质量
&lt;/h3&gt;&lt;p&gt;想把质量做高，就得尽量保证真正可用的上游模型、足够的额度、尽可能少的抽风和降级。这件事本身就不便宜。&lt;/p&gt;
&lt;h3 id=&#34;稳定&#34;&gt;稳定
&lt;/h3&gt;&lt;p&gt;想把稳定性做上来，就得处理更多的风控、账号损耗、网络波动、限流和备份线路，甚至需要自己做更复杂的调度与兜底。这些都是成本。&lt;/p&gt;
&lt;h3 id=&#34;划算&#34;&gt;划算
&lt;/h3&gt;&lt;p&gt;可一旦你真把前两件事做扎实了，价格就不可能继续压得那么低。还能长期维持超低价，往往说明成本并没有被真正解决，只是被后移了，或者被分摊给了未来某一次集体失效。&lt;/p&gt;
&lt;p&gt;所以很多中转站看起来像是在做“高性价比”，实际上更像是在做一种脆弱平衡。风平浪静的时候它成立，一旦上游风控变严，这种平衡就很容易塌。&lt;/p&gt;
&lt;h2 id=&#34;上周末基本算是明牌了&#34;&gt;上周末，基本算是明牌了
&lt;/h2&gt;&lt;p&gt;真正让我下决心放弃继续折腾的，就是 &lt;code&gt;2026-03-28&lt;/code&gt; 到 &lt;code&gt;2026-03-29&lt;/code&gt; 这轮变化。&lt;/p&gt;
&lt;p&gt;我自己的体感是，ChatGPT 相关通道在那两天出现了很明显的集中收紧，Claude 侧的风控也在同步加强。以前还能靠切线路、切模型、切套餐勉强维持的那种“凑合能用”，突然就不成立了。&lt;/p&gt;
&lt;p&gt;这里我不想把话说成“全行业已经完全死亡”这种绝对判断，毕竟总会有人说自己手里还有个别渠道能用。但从普通用户的实际使用价值来说，低价 API 中转这条路，至少已经不再值得我继续投入时间。&lt;/p&gt;
&lt;p&gt;便宜这件事，只有在“能稳定完成任务”的前提下才有意义。如果连最基本的可靠性都保不住，低价就会变成一种错觉。&lt;/p&gt;
&lt;h2 id=&#34;为什么这种模式本来就很脆弱&#34;&gt;为什么这种模式本来就很脆弱
&lt;/h2&gt;&lt;p&gt;这类模式本来就站在很脆弱的基础上。&lt;/p&gt;
&lt;p&gt;OpenAI 和 Anthropic 这些厂商，本来就不是按照“二次分发灰色转售”的逻辑设计产品的。官方条款里对 Key 转卖、绕过限制、逆向、规避保护措施，本来就有明确限制。OpenAI 的服务协议里直接把“买卖或转让 API Key”“绕过速率限制或保护措施”“规避使用限制”列为不允许的行为；Anthropic 的商业条款也明确保留了对违规使用、非授权接入和服务滥用的约束空间。&lt;/p&gt;
&lt;p&gt;换句话说，这些中转站并不是在官方鼓励的生态里做创新，而是在官方治理边界的缝隙里讨生活。只要模型厂商开始认真清理，这种模式天然就会先挨打。&lt;/p&gt;
&lt;p&gt;另外还有一个很现实的背景：很多国外模型服务本来就有地区、支付和账户体系上的门槛。官方支持地区有限，很多用户因此被挡在门外，这才催生了中转需求。但需求存在，不代表模式就稳固。它只能说明，灰色替代方案有市场，不代表它有长期确定性。&lt;/p&gt;
&lt;h2 id=&#34;收尾&#34;&gt;收尾
&lt;/h2&gt;&lt;p&gt;折腾了一圈以后，我现在的结论反而简单了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;codex&lt;/code&gt; 的性价比反而更高，至少从我自己的使用体验看，它更适合真正落到生产环境里。没有听到太多国内大面积封号的反馈，整体心智负担也更低。&lt;/p&gt;
&lt;p&gt;当然，&lt;code&gt;codex&lt;/code&gt; 也不是没有约束。五小时限额、周限额摆在那里，我现在用它的时候，反而不会再像以前那样肆无忌惮，什么都问，什么都丢给它处理。很多问题，会先在脑子里过一遍，先自己拆一下，再决定要不要把这次额度花出去。&lt;/p&gt;
&lt;p&gt;这么看，限额未必全是坏事。它客观上逼着人脑重新参与进来，逼着你先整理问题、先做判断、先做取舍，而不是把所有思考都外包出去。以前我也写过，模型稍微“弱智”一点，不见得全是坏事，因为它会倒逼使用者保持基本的思考强度。现在回头看，这个判断依然成立。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Claude&lt;/code&gt; 我暂时不考虑了。不是说它能力不强，恰恰相反，它依然很强。但连个人正规购买都存在封号的不确定性，那它就不适合作为我当前阶段的主力方案。&lt;/p&gt;
&lt;p&gt;国内模型这边，我选择继续观察。现在还没到我愿意重仓长期绑定的时候。&lt;/p&gt;
&lt;p&gt;所以最后还是回到了一个很朴素的选择：买了 &lt;code&gt;ChatGPT Plus&lt;/code&gt;，先凑合用着，继续看大模型行业后面怎么演化。很多时候，最便宜的方案未必最省钱，最省心的方案，反而才是真正的性价比。&lt;/p&gt;
&lt;h2 id=&#34;参考链接&#34;&gt;参考链接
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;OpenAI 支持地区：https://help.openai.com/en/articles/5347006-which-countries-and-territories-are-supported-by-openai&lt;/li&gt;
&lt;li&gt;OpenAI Services Agreement：https://openai.com/policies/services-agreement/&lt;/li&gt;
&lt;li&gt;Anthropic Commercial Terms：https://www.anthropic.com/legal/commercial-terms&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>unordered_map 查得到却取错值，一次静态 lambda 的内存事故</title>
        <link>https://ttf248.life/p/deep-dive-into-memory-corruption-and-cache-pollution-caused-by-static-lambdas-in-c/</link>
        <pubDate>Mon, 16 Mar 2026 20:18:18 +0800</pubDate>
        
        <guid>https://ttf248.life/p/deep-dive-into-memory-corruption-and-cache-pollution-caused-by-static-lambdas-in-c/</guid>
        <description>&lt;p&gt;有一种 C++ 问题特别容易把人带偏：&lt;code&gt;unordered_map::find&lt;/code&gt; 明明命中了，&lt;code&gt;it-&amp;gt;first&lt;/code&gt; 也对，可你再看 &lt;code&gt;it-&amp;gt;second&lt;/code&gt; 里面的字段，像是变成了另一个 key 的数据。&lt;/p&gt;
&lt;p&gt;第一反应通常是怀疑 map 坏了，或者 hash 冲突把数据弄乱了。可 &lt;code&gt;unordered_map&lt;/code&gt; 没那么玄。查找命中以后，key 和 value 对不上，更常见的原因不是容器失灵，而是你在更新 value 的时候已经写错了地方。&lt;/p&gt;
&lt;p&gt;这篇记录的坑，就是函数内部的 &lt;code&gt;static lambda&lt;/code&gt; 捕获了局部引用。第一轮看不出来，后面再调用时，lambda 还握着第一次调用留下的引用，问题就开始变得像“缓存串号”。&lt;/p&gt;
&lt;h2 id=&#34;先看那个很像优化的小动作&#34;&gt;先看那个很像优化的小动作
&lt;/h2&gt;&lt;p&gt;问题代码大概长这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;UpdateCache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unordered_map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Tick&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Tick&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;patch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;](&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;volume&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;volume&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;patch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;写这段代码的人可能不是想偷懒，而是想少创建一次 lambda。&lt;code&gt;patch&lt;/code&gt; 看起来只是一个小更新函数，放成 &lt;code&gt;static&lt;/code&gt; 好像还能省点开销。&lt;/p&gt;
&lt;p&gt;真正危险的是这两个东西叠在一起：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;static&lt;/code&gt; 让这个 lambda 只初始化一次，并且活到进程结束；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[&amp;amp;]&lt;/code&gt; 让它捕获当前栈帧里的局部引用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;第一次调用 &lt;code&gt;UpdateCache&lt;/code&gt; 时，&lt;code&gt;patch&lt;/code&gt; 被初始化。它捕获到的 &lt;code&gt;current_input&lt;/code&gt;，不是一个抽象名字，而是第一次调用时那块栈上的对象引用。&lt;/p&gt;
&lt;p&gt;函数返回以后，那块栈帧结束了。第二次再调用，&lt;code&gt;patch&lt;/code&gt; 不会重新初始化，它还拿着第一次的引用。后面发生什么，就已经进入未定义行为。&lt;/p&gt;
&lt;h2 id=&#34;为什么现象会像-map-查错了&#34;&gt;为什么现象会像 map 查错了
&lt;/h2&gt;&lt;p&gt;这个 bug 迷惑人的地方在于，&lt;code&gt;find&lt;/code&gt; 可能一直是对的。&lt;/p&gt;
&lt;p&gt;你用当前输入的 &lt;code&gt;symbol&lt;/code&gt; 去查 map，查到了正确的 iterator。到这一步，容器没有错。错发生在后面的更新函数里：它读到的 &lt;code&gt;current_input.price&lt;/code&gt;、&lt;code&gt;current_input.volume&lt;/code&gt;，可能已经不是当前这次调用的输入。&lt;/p&gt;
&lt;p&gt;如果那块失效栈内存刚好被复用了，你看到的现象就会很奇怪：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;key 是 A；&lt;/li&gt;
&lt;li&gt;iterator 也确实指向 A；&lt;/li&gt;
&lt;li&gt;但写进去的字段像来自 B；&lt;/li&gt;
&lt;li&gt;偶尔还会因为栈布局变化表现不稳定。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就很容易让人误判成“map 里缓存污染了”。其实污染不是 map 自己产生的，而是更新逻辑从一个悬空引用里拿了数据。&lt;/p&gt;
&lt;p&gt;更糟的是，这类问题不一定马上崩。它可能只是偶尔错字段、偶尔错价格、偶尔在高并发或不同编译优化下变得更明显。越不像崩溃，越容易被当成业务数据问题。&lt;/p&gt;
&lt;h2 id=&#34;不要用-static-保存带捕获的局部闭包&#34;&gt;不要用 static 保存带捕获的局部闭包
&lt;/h2&gt;&lt;p&gt;这条经验可以说得很直接：函数内部的 &lt;code&gt;static lambda&lt;/code&gt;，如果捕获局部变量，优先当成危险代码处理。&lt;/p&gt;
&lt;p&gt;安全写法是让 lambda 无状态，依赖都通过参数传进去：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;UpdateCache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unordered_map&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Tick&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Tick&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;patch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[](&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Tick&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;volume&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;volume&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;patch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果真想避免重复写这段更新逻辑，可以把它提成普通函数，或者写成不捕获任何局部状态的 lambda：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;patch&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[](&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Tick&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;price&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;volume&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;volume&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&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;static&lt;/code&gt; 这个词绝对不能出现，而在于它不能把短生命周期对象悄悄带进长生命周期闭包里。&lt;/p&gt;
&lt;h2 id=&#34;排查时先证明-key-和-value-的身份一致&#34;&gt;排查时先证明 key 和 value 的身份一致
&lt;/h2&gt;&lt;p&gt;遇到“查到了但值不对”的问题，我现在会先加几个很笨但有效的检查。&lt;/p&gt;
&lt;p&gt;比如更新前后都确认身份：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;auto&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;find&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;it&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;patch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;current_input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;assert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;first&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;it&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;symbol&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些断言不是为了修 bug，而是为了缩小现场：到底是 &lt;code&gt;find&lt;/code&gt; 找错了，还是更新以后 value 被写错了。&lt;/p&gt;
&lt;p&gt;测试环境还应该打开工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AddressSanitizer&lt;/code&gt; 用来抓失效栈内存访问；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UndefinedBehaviorSanitizer&lt;/code&gt; 用来抓未定义行为；&lt;/li&gt;
&lt;li&gt;如果和线程有关，再补 &lt;code&gt;ThreadSanitizer&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果 sanitizer 一开就报 use-after-scope 或类似问题，就别再围着 hash、bucket、rehash 打转了。容器通常只是案发现场，不一定是凶手。&lt;/p&gt;
&lt;h2 id=&#34;真正的教训&#34;&gt;真正的教训
&lt;/h2&gt;&lt;p&gt;这类问题的教训不只是“不要写 static lambda”。&lt;/p&gt;
&lt;p&gt;更准确地说，是不要让一个长生命周期对象，偷偷引用短生命周期数据。&lt;code&gt;static&lt;/code&gt;、全局回调、缓存下来的函数对象、异步任务，都会把这个问题放大。&lt;/p&gt;
&lt;p&gt;C++ 里很多诡异现象，本质上不是语法难，而是生命周期边界没有写清楚。代码表面看起来像一次小优化，实际是在把第一次调用的栈帧钉到进程生命周期里。&lt;/p&gt;
&lt;p&gt;所以以后再遇到 &lt;code&gt;unordered_map&lt;/code&gt; 查到了但 value 像串号，我会先问一句：&lt;/p&gt;
&lt;p&gt;是不是有人在更新 value 的时候，拿了一个已经不该存在的引用？&lt;/p&gt;
&lt;p&gt;这比先怀疑容器本身靠谱得多。&lt;/p&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.cppreference.com/w/cpp/language/lambda&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;cppreference: Lambda expressions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.cppreference.com/w/cpp/language/storage_duration&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;cppreference: Storage duration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://clang.llvm.org/docs/AddressSanitizer.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Clang AddressSanitizer documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Clang UndefinedBehaviorSanitizer documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;原始提示词&#34;&gt;原始提示词
&lt;/h3&gt;&lt;h3 id=&#34;写作思路摘要&#34;&gt;写作思路摘要
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;原文没有保留原始提示词；本次重写只基于旧文中的问题描述、示例代码和修复方向。&lt;/li&gt;
&lt;li&gt;把旧稿的“深度解析”口吻改成排障记录，先让读者看到为什么像 &lt;code&gt;unordered_map&lt;/code&gt; 查错了。&lt;/li&gt;
&lt;li&gt;保留核心技术判断：&lt;code&gt;static lambda&lt;/code&gt; 配合引用捕获会把短生命周期引用带进长生命周期闭包。&lt;/li&gt;
&lt;li&gt;补充排查顺序和 sanitizer 工具，避免只停留在概念解释。&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>从协议约束到智能释放：深度对比 MCP 与 Skill</title>
        <link>https://ttf248.life/p/mcp-vs-skill-comparison/</link>
        <pubDate>Fri, 30 Jan 2026 01:53:28 +0800</pubDate>
        
        <guid>https://ttf248.life/p/mcp-vs-skill-comparison/</guid>
        <description>&lt;p&gt;在 AI Agent 领域，如何让大语言模型（LLM）更安全、更高效地与外部世界交互，一直是开发者关注的核心课题。近期，Anthropic 推出的 &lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt; 和 Gemini CLI 中的 &lt;strong&gt;Skill&lt;/strong&gt; 机制成为了讨论的热点。&lt;/p&gt;
&lt;p&gt;这两者虽然都旨在扩展模型的能力边界，但在设计哲学、信任边界以及约束机制上有着截然不同的路径。&lt;/p&gt;
&lt;h2 id=&#34;1-什么是-mcpmodel-context-protocol&#34;&gt;1. 什么是 MCP？（Model Context Protocol）
&lt;/h2&gt;&lt;p&gt;MCP 是由 Anthropic 推出的一种开放标准。它的核心思想是&lt;strong&gt;标准化&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;架构&lt;/strong&gt;：采用典型的客户端-服务器架构。MCP 服务器提供数据或工具，客户端（如 Claude Desktop）则负责将这些能力暴露给模型。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;约束&lt;/strong&gt;：极高。所有可用的工具、资源和提示词必须通过严格的 JSON Schema 进行预定义。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全性&lt;/strong&gt;：通过标准化的接口，开发者可以清晰地定义模型能做什么、不能做什么，这种“协议级”的约束保证了系统的可预测性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;2-什么是-skillgemini-cli-视角&#34;&gt;2. 什么是 Skill？（Gemini CLI 视角）
&lt;/h2&gt;&lt;p&gt;相对于 MCP 的严谨，Gemini CLI 中的 &lt;strong&gt;Skill&lt;/strong&gt; 更像是一种&lt;strong&gt;赋能&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心逻辑&lt;/strong&gt;：Skill 往往是一组精心设计的指令集（System Prompts）和特定工具的组合。通过 &lt;code&gt;activate_skill&lt;/code&gt;，模型进入一种“专家模式”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;约束&lt;/strong&gt;：相对较少。它更信任模型本身的推理和执行能力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;灵活性&lt;/strong&gt;：类似于之前的“自定命令”，但它不仅是简单的别名，而是包含了复杂的上下文和任务执行逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;3-核心差异信任度的不同&#34;&gt;3. 核心差异：信任度的不同
&lt;/h2&gt;&lt;p&gt;MCP 与 Skill 最本质的区别在于对“模型能力”的信任偏向。&lt;/p&gt;
&lt;h3 id=&#34;mcp信任协议&#34;&gt;MCP：信任协议
&lt;/h3&gt;&lt;p&gt;MCP 的逻辑是：&lt;strong&gt;“我不完全信任模型的自主性，但我信任这套协议。”&lt;/strong&gt;
在 MCP 框架下，模型必须在既定的轨道上运行。如果协议没定义某种交互方式，模型就无法执行。这对于企业级应用和需要高度合规性的场景非常友好。&lt;/p&gt;
&lt;h3 id=&#34;skill信任模型&#34;&gt;Skill：信任模型
&lt;/h3&gt;&lt;p&gt;Skill 的逻辑是：&lt;strong&gt;“我信任模型的能力，我只需要告诉它目标和可用资源。”&lt;/strong&gt;
Skill 的约束更少，它允许模型在执行任务时有更大的发挥空间。例如，一个 &lt;code&gt;skill-creator&lt;/code&gt; 可以指导模型如何构建新的技能，这其中涉及到大量的逻辑判断和策略选择，而非简单的接口调用。&lt;/p&gt;
&lt;h2 id=&#34;4-深度对比表&#34;&gt;4. 深度对比表
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;特性&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;MCP (Model Context Protocol)&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;Skill (Gemini CLI)&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;设计重心&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;标准化、互操作性&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;任务导向、能力增强&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;约束机制&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;强约束 (Schema-based)&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;弱约束 (Instruction-based)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;信任模型&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;信任协议定义的边界&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;信任模型的自主推理能力&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;灵活性&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;较低（需遵循标准）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;极高（可动态解释指令）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;典型应用场景&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;跨平台工具集成、数据源接入&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;复杂任务规划、专家级能力扩展&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;5-总结与展望&#34;&gt;5. 总结与展望
&lt;/h2&gt;&lt;p&gt;MCP 代表了 AI 基础设施的“工业化”趋势——通过统一的接口降低集成成本。而 Skill 则展示了 AI Agent 的“智能化”方向——通过更少的束缚释放模型作为“数字员工”的潜力。&lt;/p&gt;
&lt;p&gt;对于开发者而言，选择哪种方案取决于你的应用场景：如果你需要构建一个可大规模复用的工具生态，MCP 是不二之选；如果你希望打造一个能够处理复杂、多变任务的智能助手，那么赋予它更强大的 Skill 显然更具杀伤力。&lt;/p&gt;</description>
        </item>
        <item>
        <title>wrk 与 JMeter 的深度较量</title>
        <link>https://ttf248.life/p/wrk-vs-jmeter-deep-benchmarking/</link>
        <pubDate>Fri, 19 Dec 2025 01:14:49 +0800</pubDate>
        
        <guid>https://ttf248.life/p/wrk-vs-jmeter-deep-benchmarking/</guid>
        <description>&lt;p&gt;在互联网系统的压力测试中，我们经常会遇到两款风格迥异的工具：一个是极其轻量、追求极致吞吐量的 wrk；另一个是功能大而全、模拟真实业务流的 JMeter。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;提示词：梳理核心思路，撰写科普文章：http 压力测试工具，wrk vs Jmeter 有什么不同，我了解到的，wrk 倾向于 一个线程多个 connect 进行测试，Jmeter 更多是短连接模式，能通过配置调整为长联机&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;核心架构多线程-vs-事件驱动&#34;&gt;核心架构：多线程 vs 事件驱动
&lt;/h2&gt;&lt;p&gt;这是两者性能差距的根本原因。&lt;/p&gt;
&lt;h3 id=&#34;1-jmeter传统的一人一岗制-thread-per-request&#34;&gt;1. JMeter：传统的“一人一岗”制 (Thread-per-Request)
&lt;/h3&gt;&lt;p&gt;JMeter 基于 Java 开发，采用的是经典的&lt;strong&gt;多线程模型&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;逻辑：&lt;/strong&gt; 每一个并发用户（Virtual User）都对应 JVM 中的一个物理线程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代价：&lt;/strong&gt; 线程是非常昂贵的资源。当并发数达到几千时，上下文切换（Context Switch）和内存消耗会显著拖慢测试机本身，导致“压测机还没把服务器压死，自己先崩了”的现象。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-wrk现代的多面手制-event-driven&#34;&gt;2. wrk：现代的“多面手”制 (Event-driven)
&lt;/h3&gt;&lt;p&gt;wrk 采用 C 语言编写，核心逻辑基于 Redis 同款的 &lt;code&gt;ae&lt;/code&gt; 事件循环框架（利用 &lt;code&gt;epoll/kqueue&lt;/code&gt;）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;逻辑：&lt;/strong&gt; wrk 并不为每个连接创建线程。它只启动极少数的线程（通常等于你的 CPU 核心数），每个线程内部通过&lt;strong&gt;非阻塞 I/O&lt;/strong&gt; 同时管理成千上万个连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势：&lt;/strong&gt; 这就是你提到的“一个线程多个 connection”。它极大地减少了线程切换开销，单机即可跑出百万级别的 RPS（每秒请求数）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;连接模型短连接-vs-长连接&#34;&gt;连接模型：短连接 vs 长连接
&lt;/h2&gt;&lt;p&gt;关于你提到的连接模式，这里有更深层的细节：&lt;/p&gt;
&lt;h3 id=&#34;1-jmeter-的重与灵&#34;&gt;1. JMeter 的“重”与“灵”
&lt;/h3&gt;&lt;p&gt;JMeter 默认确实更倾向于模拟真实用户的行为。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;短连接倾向：&lt;/strong&gt; 在默认配置下，JMeter 的某些旧版本或特定配置可能不会积极复用连接，导致大量的 TCP 握手。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可调校性：&lt;/strong&gt; 你可以通过在 &lt;code&gt;HTTP Request&lt;/code&gt; 中勾选 &lt;strong&gt;&amp;ldquo;KeepAlive&amp;rdquo;&lt;/strong&gt;，或者在 &lt;code&gt;user.properties&lt;/code&gt; 中调整连接池参数来开启长连接。但即便是长连接，受限于线程模型，它也难以维持数十万级别的并发长连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-wrk-的快与狠&#34;&gt;2. wrk 的“快”与“狠”
&lt;/h3&gt;&lt;p&gt;wrk 设计的初衷就是为了测试 &lt;strong&gt;HTTP Keep-Alive&lt;/strong&gt; 的性能。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;长连接策略：&lt;/strong&gt; wrk 在测试开始时会建立好指定的连接数（&lt;code&gt;-c&lt;/code&gt; 参数），并在整个测试过程中尽可能复用这些连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用场景：&lt;/strong&gt; 它非常适合测试 Nginx、网关（Gateway）或高并发 API 在极端长连接压力下的吞吐极限。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;深度对比表&#34;&gt;深度对比表
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;特性&lt;/th&gt;
					&lt;th&gt;wrk&lt;/th&gt;
					&lt;th&gt;Apache JMeter&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;开发语言&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;C / Lua (脚本)&lt;/td&gt;
					&lt;td&gt;Java (GUI)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;并发模型&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;事件驱动 (epoll/kqueue)&lt;/td&gt;
					&lt;td&gt;多线程 (Thread-per-user)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;资源消耗&lt;/strong&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;&lt;strong&gt;业务复杂度&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;较低，主要针对单个 URL&lt;/td&gt;
					&lt;td&gt;极高，支持多步脚本、断言、提取器&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;测试场景&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;静态 API 压测、容量评估&lt;/td&gt;
					&lt;td&gt;复杂业务链路、功能回归压测&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;报表能力&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;仅文本摘要&lt;/td&gt;
					&lt;td&gt;极丰富，支持各类图表和 HTML 报告&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;总结我该选哪一个&#34;&gt;总结：我该选哪一个？
&lt;/h2&gt;&lt;p&gt;这两款工具并不是替代关系，而是互补关系：&lt;/p&gt;
&lt;h3 id=&#34;选-wrk&#34;&gt;选 wrk
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;想要测试服务器的&lt;strong&gt;极限吞吐量&lt;/strong&gt;（RPS）。&lt;/li&gt;
&lt;li&gt;测试对象是单一的 API 或静态资源。&lt;/li&gt;
&lt;li&gt;希望用最少的测试服务器压出最大的流量。&lt;/li&gt;
&lt;li&gt;熟悉 Lua 脚本来定制化请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;选-jmeter&#34;&gt;选 JMeter
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;需要模拟&lt;strong&gt;复杂的业务流程&lt;/strong&gt;（如：登录 -&amp;gt; 搜商品 -&amp;gt; 下单 -&amp;gt; 支付）。&lt;/li&gt;
&lt;li&gt;需要可视化的界面来观察响应时间分布、错误率等详细指标。&lt;/li&gt;
&lt;li&gt;测试需要处理动态参数（如从上一个接口提取 Token 传递给下一个接口）。&lt;/li&gt;
&lt;li&gt;团队更习惯使用图形化工具而非命令行。&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>详解 Passkey（通行密钥）的工作原理与未来</title>
        <link>https://ttf248.life/p/detailed-explanation-of-how-passkeys-work-and-their-future/</link>
        <pubDate>Thu, 04 Dec 2025 22:14:06 +0800</pubDate>
        
        <guid>https://ttf248.life/p/detailed-explanation-of-how-passkeys-work-and-their-future/</guid>
        <description>&lt;p&gt;背景介绍，腾讯的 cnb 平台仅支持微信登录，没有常规的邮箱账号方式，导致群里每天都有几个哥们念叨这事，看着都烦了，腾讯的产品经理弄了个折中的方案：支持Passkey登录。&lt;/p&gt;
&lt;p&gt;每天，我们都在重复一个危险的动作：输入密码。尽管我们有复杂的规则（大小写、特殊符号、数字），但数据泄露、钓鱼攻击和“忘记密码”的烦恼依然困扰着每一个人。&lt;/p&gt;
&lt;p&gt;科技巨头们（Apple, Google, Microsoft）联合 FIDO 联盟给出了终极解决方案：&lt;strong&gt;Passkey（通行密钥）&lt;/strong&gt;。它不仅仅是“替代”密码，而是彻底“消灭”了密码。&lt;/p&gt;
&lt;p&gt;登录流程从验证密码，转成了验证&lt;strong&gt;当前设备&lt;/strong&gt;是否可信。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;详解 passkey 的工作原理，怎么能用于登录管理，整理相关内容，输出文章用于互联网发布&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;什么是-passkey&#34;&gt;什么是 Passkey？
&lt;/h2&gt;&lt;p&gt;简单来说，&lt;strong&gt;Passkey 是一种存储在您设备上的数字凭证&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它可以替代传统的“用户名 + 密码”。当你登录支持 Passkey 的网站（如 Google、GitHub、Adobe）时，你不需要输入任何字符，只需要通过面部识别（Face ID）、指纹（Touch ID）或设备 PIN 码验证一下，就能瞬间登录。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;核心区别：&lt;/strong&gt; 密码是&lt;strong&gt;你记住的一串字符&lt;/strong&gt;（容易被偷、被猜、被忘）；Passkey 是&lt;strong&gt;你拥有的一项资产&lt;/strong&gt;（加密密钥，存储在设备硬件中）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;passkey-的工作原理非对称加密&#34;&gt;Passkey 的工作原理：非对称加密
&lt;/h2&gt;&lt;p&gt;Passkey 的背后是 &lt;strong&gt;WebAuthn&lt;/strong&gt; 标准和 &lt;strong&gt;FIDO2&lt;/strong&gt; 协议。为了理解它，我们需要了解&lt;strong&gt;公钥密码学（Public Key Cryptography）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;想象 Passkey 是一对“钥匙”和“锁”：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;私钥 (Private Key)：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;保存在哪里？&lt;/strong&gt; 安全地存储在你的设备端（如 iPhone 的安全芯片、电脑的 TPM 模块或密码管理器中）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特性：&lt;/strong&gt; 极其敏感，&lt;strong&gt;永远不会发送给服务器&lt;/strong&gt;，也不会离开你的设备。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用：&lt;/strong&gt; 它是你的“电子签名笔”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;公钥 (Public Key)：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;保存在哪里？&lt;/strong&gt; 上传并存储在网站/APP 的服务器上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特性：&lt;/strong&gt; 公开可见，不敏感。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;作用：&lt;/strong&gt; 它是用来验证你签名的“验钞机”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;登录流程详解-the-handshake&#34;&gt;登录流程详解 (The Handshake)
&lt;/h3&gt;&lt;p&gt;当你使用 Passkey 登录时，发生了一次精妙的“挑战-应答”机制：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;发起登录：&lt;/strong&gt; 你点击“登录”，网站服务器向你的设备发送一个&lt;strong&gt;随机数学难题（Challenge）&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本地验证：&lt;/strong&gt; 你的手机/电脑弹出提示，要求你使用生物识别（刷脸/指纹）解锁。
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;注意：这一步只是为了授权设备使用私钥，生物信息本身不会上传。&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数字签名：&lt;/strong&gt; 解锁成功后，设备使用&lt;strong&gt;私钥&lt;/strong&gt;对那个数学难题进行“签名”，并将签名结果发回服务器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务器验证：&lt;/strong&gt; 服务器使用你之前留下的&lt;strong&gt;公钥&lt;/strong&gt;来验证这个签名。如果通过，服务器就确认了“通过验证的人确实持有私钥”，从而允许登录。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;为什么-passkey-比密码安全得多&#34;&gt;为什么 Passkey 比密码安全得多？
&lt;/h2&gt;&lt;p&gt;Passkey 解决了传统密码的三大死穴：&lt;/p&gt;
&lt;h3 id=&#34;彻底免疫钓鱼攻击-anti-phishing&#34;&gt;彻底免疫“钓鱼攻击” (Anti-Phishing)
&lt;/h3&gt;&lt;p&gt;这是 Passkey 最强悍的功能。Passkey 协议中强制包含了&lt;strong&gt;来源绑定（Origin Binding）&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;场景：&lt;/strong&gt; 黑客做了一个假的 &lt;code&gt;g00gle.com&lt;/code&gt; 诱骗你登录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结果：&lt;/strong&gt; 你的浏览器和系统会发现当前域名与 Passkey 注册的域名 &lt;code&gt;google.com&lt;/code&gt; 不匹配，&lt;strong&gt;拒绝发起认证&lt;/strong&gt;。你甚至都没有机会“手滑”输入密码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;服务器泄露也没用&#34;&gt;服务器泄露也没用
&lt;/h3&gt;&lt;p&gt;即使黑客攻破了 Google 的服务器，拿走了所有的数据库，他们得到的也只是&lt;strong&gt;公钥&lt;/strong&gt;。
公钥无法用来推导出私钥。黑客拿着公钥就像拿着一把锁，但他没有开锁的钥匙（私钥在你手机里），因此无法登录你的账户。&lt;/p&gt;
&lt;h3 id=&#34;没有弱密码&#34;&gt;没有“弱密码”
&lt;/h3&gt;&lt;p&gt;用户不再需要设置“123456”这种弱密码，因为密钥是由算法生成的强加密数据。&lt;/p&gt;
&lt;h2 id=&#34;passkey-如何改变登录管理&#34;&gt;Passkey 如何改变“登录管理”？
&lt;/h2&gt;&lt;p&gt;过去，我们依赖 1Password、LastPass 或 Chrome 浏览器来记录一长串字符。现在，登录管理正在发生质变。&lt;/p&gt;
&lt;h3 id=&#34;跨设备同步-passkey-sync&#34;&gt;跨设备同步 (Passkey Sync)
&lt;/h3&gt;&lt;p&gt;早期的硬件密钥（如 YubiKey）很容易丢。现在的 Passkey 支持云端同步：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Apple 生态：&lt;/strong&gt; 通过 iCloud 钥匙串同步。你在 iPhone 上创建的 Passkey，Mac 上自动可用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google 生态：&lt;/strong&gt; 通过 Google 密码管理器同步 Android 和 Chrome。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三方管理：&lt;/strong&gt; 1Password、Dashlane 等工具也全面支持 Passkey。这意味着你可以跨生态（在 Windows 电脑上使用保存在 iPhone 里的 Passkey）进行登录。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;跨设备登录-cross-device-auth-via-qr-code&#34;&gt;跨设备登录 (Cross-Device Auth via QR Code)
&lt;/h3&gt;&lt;p&gt;如果你想在网吧的电脑（Windows）上登录，但你的 Passkey 在 iPhone 里，怎么办？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;网页上选择“使用另一台设备登录”。&lt;/li&gt;
&lt;li&gt;屏幕显示一个二维码（FIDO Cross-Device Flow）。&lt;/li&gt;
&lt;li&gt;用 iPhone 相机扫描二维码。&lt;/li&gt;
&lt;li&gt;手机通过蓝牙与电脑建立近场连接（证明你在现场），并进行生物验证。&lt;/li&gt;
&lt;li&gt;电脑端登录成功。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;从管理秘密到管理信任&#34;&gt;从“管理秘密”到“管理信任”
&lt;/h3&gt;&lt;p&gt;未来的登录管理不再是查看一个个明文密码，而是&lt;strong&gt;管理受信任的设备&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你可以查看：“我的 GitHub 账户绑定了我的 iPhone 和 MacBook”。&lt;/li&gt;
&lt;li&gt;如果不慎丢失手机，你只需在服务端（或云端账户）撤销该设备的公钥访问权即可。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;表格对比密码-vs-passkey&#34;&gt;表格对比：密码 vs. Passkey
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;维度&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;传统密码 (Passwords)&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;通行密钥 (Passkeys)&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;记忆负担&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;高（需记忆复杂字符）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;无（无需记忆）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;钓鱼风险&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;极高（易受欺骗输入）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;零&lt;/strong&gt;（域名强制绑定）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;服务器泄露&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;危险（需撞库/改密）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;安全（泄露公钥无影响）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;登录体验&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;慢（输入或复制粘贴）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;快（一键生物验证）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;依赖性&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;依赖大脑或密码本&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;依赖设备（手机/电脑）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;现在的挑战与未来&#34;&gt;现在的挑战与未来
&lt;/h2&gt;&lt;p&gt;尽管 Passkey 极其美好，但普及仍需时间：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;平台壁垒：&lt;/strong&gt; 虽然标准统一，但 Apple、Google 和 Microsoft 在各自生态内的体验最丝滑，跨生态流转（如 Android 手机配 iPad）虽然可行，但仍有轻微摩擦。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设备依赖：&lt;/strong&gt; 如果你丢失了所有受信任的设备且没有云备份，找回账户会比较麻烦（通常需要备用的恢复码）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;旧系统兼容：&lt;/strong&gt; 许多老旧网站和公司内网尚未支持 WebAuthn 标准。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结
&lt;/h2&gt;&lt;p&gt;Passkey 不是密码的升级版，它是互联网身份验证的一次&lt;strong&gt;底层重构&lt;/strong&gt;。它利用现代设备的生物识别能力和公钥加密技术，将安全性提升到了金融级别，同时把用户体验简化到了极致。&lt;/p&gt;
&lt;p&gt;对于普通用户来说，尽快在支持的平台（Google, Apple, Microsoft, Amazon 等）开启 Passkey，是提升个人数字安全性价比最高的投资。&lt;/p&gt;</description>
        </item>
        <item>
        <title>AI 变“笨”了吗？揭秘参数精度与推理成本的博弈</title>
        <link>https://ttf248.life/p/is-ai-getting-dumber-uncovering-the-game-between-parameter-precision-and-inference-costs/</link>
        <pubDate>Thu, 04 Dec 2025 21:58:09 +0800</pubDate>
        
        <guid>https://ttf248.life/p/is-ai-getting-dumber-uncovering-the-game-between-parameter-precision-and-inference-costs/</guid>
        <description>&lt;p&gt;近期混迹在各个编程大模型的交流圈，模型降智是大家吐槽最多的事情。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地台式机部署的都是量化以后的模型，可以说是降智后的版本&lt;/li&gt;
&lt;li&gt;vibe coding 这么热门，有没有可能当前大模型输出的内容，代码是最值钱的产物？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本轮的提示词有过一次优化，刚好是模型降智，大模型给的方案是优化提示词，更详细的任务规划、更明确的输出要求&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;针对这个问题，给出合适的提示词：现在很多大厂都提供了大模型服务，偶尔会有用户反馈模型降智问题；从专业角度，参数的精度、推理的成本出发，编写文章；科普文，不要太长。&lt;/p&gt;
&lt;p&gt;优化后的版本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;你是一位资深的 AI 行业技术专栏作家。你的目标是撰写一篇针对普通读者但内容专业的中文科普文章。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;请一步一步思考并撰写：
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1. 大纲制定：首先，请拟定一个清晰的三段式文章结构大纲（例如：引言、精度分析、成本与架构分析、结论）。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2. 内容聚焦：文章必须深入浅出地解释用户反馈的“模型降智”现象，核心分析点必须围绕 **参数精度（量化）** 和 **推理的运营成本** 展开。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3. 字数与格式：最终文章长度必须控制在 &lt;span class=&#34;m&#34;&gt;600&lt;/span&gt; 字以内。请使用清晰的 &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;##` 标题 来划分段落，并使用 **粗体** 来强调文中的关键专业术语（如：量化、MoE、FP16）。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;引言&#34;&gt;引言
&lt;/h2&gt;&lt;p&gt;为了确保逻辑清晰且符合科普要求，我制定了以下结构：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;引言：&lt;/strong&gt; 确认用户感知的真实性，引出核心矛盾——在用户规模激增的背景下，服务商必须在“智力巅峰”与“服务可用性”之间做平衡。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;精度分析（核心点1）：&lt;/strong&gt; 解释&lt;strong&gt;参数&lt;/strong&gt;与&lt;strong&gt;精度&lt;/strong&gt;的关系。通过对比 &lt;strong&gt;FP16&lt;/strong&gt; 与 &lt;strong&gt;量化&lt;/strong&gt;（INT8/INT4），用“图片压缩”的类比来说明为什么精度下降会导致逻辑变差。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;成本与架构（核心点2）：&lt;/strong&gt; 剖析&lt;strong&gt;推理成本&lt;/strong&gt;。介绍 &lt;strong&gt;MoE&lt;/strong&gt; 架构与&lt;strong&gt;稀疏激活&lt;/strong&gt;机制，解释为了省钱和加速，模型是如何“偷懒”的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结论：&lt;/strong&gt; 总结这种“降智”是商业化普及的必经之路（不可能三角）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;文章正文&#34;&gt;文章正文
&lt;/h2&gt;&lt;p&gt;近期，不少用户反馈大模型似乎没有以前“聪明”了：逻辑变简单、指令遵循变差，甚至出现车轱辘话。这并非全是错觉。从技术视角看，这种“降智”现象往往是大厂在面对海量用户时，为了平衡&lt;strong&gt;推理成本&lt;/strong&gt;与服务速度而做出的技术妥协。&lt;/p&gt;
&lt;h2 id=&#34;精度缩水从-4k-到-720p-的代价&#34;&gt;精度“缩水”：从 4K 到 720P 的代价
&lt;/h2&gt;&lt;p&gt;大模型的“智商”主要存储在数千亿个&lt;strong&gt;参数&lt;/strong&gt;中。在理想状态下，这些参数以高精度的 &lt;strong&gt;FP16&lt;/strong&gt;（16位浮点数）格式运行，能捕捉极细微的语义差别。但这种高精度意味着巨大的&lt;strong&gt;显存&lt;/strong&gt;占用和缓慢的计算速度。&lt;/p&gt;
&lt;p&gt;为了让数亿用户都能流畅使用，服务商普遍采用了&lt;strong&gt;量化&lt;/strong&gt;（Quantization）技术。这是一种将参数精度从 &lt;strong&gt;FP16&lt;/strong&gt; 压缩到 &lt;strong&gt;INT8&lt;/strong&gt; 甚至 &lt;strong&gt;INT4&lt;/strong&gt; 的手段。&lt;/p&gt;
&lt;p&gt;这就好比将一部 4K 高清电影压缩成 720P 流媒体：虽然剧情（大体逻辑）没变，但画面细节（微小的逻辑关联、复杂指令的执行细节）却丢失了。这种“有损压缩”直接导致模型在处理复杂任务时表现力下降，给用户一种“变笨”的体感。&lt;/p&gt;
&lt;h2 id=&#34;成本压力让大脑部分休息&#34;&gt;成本压力：让大脑“部分休息”
&lt;/h2&gt;&lt;p&gt;除了精度，&lt;strong&gt;推理的运营成本&lt;/strong&gt;是另一大核心因素。每次你向 AI 提问，服务器都要进行庞大的矩阵运算，电费和硬件损耗惊人。&lt;/p&gt;
&lt;p&gt;为了降低成本，现代大模型多采用 &lt;strong&gt;MoE&lt;/strong&gt;（Mixture of Experts，混合专家模型）架构。与传统模型每次都激活所有神经元不同，&lt;strong&gt;MoE&lt;/strong&gt; 采用&lt;strong&gt;稀疏激活&lt;/strong&gt;策略：针对你的问题，系统只唤醒模型中一小部分相关的“专家”网络，其余部分保持休眠。&lt;/p&gt;
&lt;p&gt;虽然这极大降低了计算量，但如果&lt;strong&gt;路由算法&lt;/strong&gt;（Router）为了负载均衡或节省算力，将你的复杂数学问题派发给了“文学专家”，或者为了速度牺牲了专家的调用数量，输出质量自然就会出现波动和下滑。&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论
&lt;/h2&gt;&lt;p&gt;因此，所谓的“降智”，本质上是 AI 从实验室走向大规模商业化时的必然阵痛。在&lt;strong&gt;参数精度&lt;/strong&gt;上的克制和&lt;strong&gt;模型架构&lt;/strong&gt;上的精打细算，是厂商为了让每个人都能用得起 AI，而在“绝对聪明”与“成本效率”之间寻找的微妙平衡。&lt;/p&gt;</description>
        </item>
        <item>
        <title>Cloudflare 突发全球网络故障，X、ChatGPT 等知名网站瘫痪，盘前股价受挫</title>
        <link>https://ttf248.life/p/cloudflare-experienced-a-global-network-outage-causing-websites-like-x-formerly-twitter-and-chatgpt-to-crash-the-stock-price-suffered-a-setback-ahead-of-the-market-open/</link>
        <pubDate>Tue, 18 Nov 2025 22:34:02 +0800</pubDate>
        
        <guid>https://ttf248.life/p/cloudflare-experienced-a-global-network-outage-causing-websites-like-x-formerly-twitter-and-chatgpt-to-crash-the-stock-price-suffered-a-setback-ahead-of-the-market-open/</guid>
        <description>&lt;p&gt;本站点主域名走了 github pages，blog 备用子域名走了的 vercel 加速，cloudflare 部署过，后台管理页面看着太老旧，劝退了。本来还在安静的水稿子，博客圈的群里消息不断，这点一般是很安静的，打开瞅瞅，cf 挂了，好多博友的网站无法打开。&lt;/p&gt;
&lt;p&gt;作为互联网的基础设施，故障持续这么长时间，不应该；股价没有大跌，是我想不到的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;cloudflare 故障，很多知名网站都无法访问，盘前股价下跌，整理相关内容，输出文章&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;2025年11月18日 (周二) 讯&lt;/strong&gt; — 今天（11月18日）美股开盘前，全球主要的互联网基础设施服务商 Cloudflare 遭遇了一次大范围的网络故障，导致全球数千个依赖其服务的知名网站和应用程序陷入瘫痪或访问困难。&lt;/p&gt;
&lt;p&gt;受此事件影响，Cloudflare (NYSE: NET) 的股价在盘前交易中应声下跌。&lt;/p&gt;
&lt;h2 id=&#34;-故障波及全球知名服务纷纷宕机&#34;&gt;🌍 故障波及全球，知名服务纷纷“宕机”
&lt;/h2&gt;&lt;p&gt;此次故障在周二早间（北美东部时间约早上7点，世界标准时间11:48左右）开始显现。全球各地的用户报告称，无法访问包括社交平台 &lt;strong&gt;X (原 Twitter)&lt;/strong&gt;、人工智能服务 &lt;strong&gt;OpenAI (ChatGPT)&lt;/strong&gt;、音乐流媒体 &lt;strong&gt;Spotify&lt;/strong&gt;、游戏平台 &lt;strong&gt;《英雄联盟》(League of Legends)&lt;/strong&gt; 和 &lt;strong&gt;Valorant&lt;/strong&gt;，以及多家加密货币交易所（如 BitMEX、DefiLlama）在内的众多热门服务。&lt;/p&gt;
&lt;p&gt;访问这些网站的用户普遍遇到了“500 Internal Server Error”（服务器内部错误）的提示。由于 Cloudflare 是互联网的“中间人”，为全球数百万网站提供内容分发（CDN）和安全防护（DDoS）服务，其网络中的任何“抖动”都会立即产生全球性的连锁反应。&lt;/p&gt;
&lt;h2 id=&#34;-市场反应迅速net-盘前股价下跌&#34;&gt;📉 市场反应迅速，(NET) 盘前股价下跌
&lt;/h2&gt;&lt;p&gt;作为互联网的关键基础设施之一，Cloudflare 服务的稳定性备受投资者关注。&lt;/p&gt;
&lt;p&gt;在故障消息传出后，资本市场迅速作出反应。Cloudflare (NYSE: NET) 的股票在周二的盘前交易中出现明显抛售。根据财经数据显示，其股价下跌了约 &lt;strong&gt;3.5% 至 4%&lt;/strong&gt;，从前一个交易日（11月17日）约 202.25 美元的收盘价，一度跌至 195 美元左右。&lt;/p&gt;
&lt;p&gt;这反映了投资者对此次服务中断的严重性及其可能对公司声誉和短期收入造成影响的担忧。&lt;/p&gt;
&lt;h2 id=&#34;-官方回应已定位问题正在修复&#34;&gt;🛠️ 官方回应：已定位问题，正在修复
&lt;/h2&gt;&lt;p&gt;Cloudflare 官方迅速在其系统状态页面上确认了这一事件。公司在北京时间今天下午（11月18日 13:09 UTC）更新状态称：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“问题已经确定，正在实施修复。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根据 Cloudflare 的报告，此次事件被定性为“全球网络 experiencing issues”（全球网络遇到问题），并伴随“widespread 500 errors”（大范围500错误）。&lt;/p&gt;
&lt;p&gt;Cloudflare 表示，他们正在努力缓解此问题，并已看到服务开始恢复，但警告称在修复工作继续进行期间，客户可能仍会观察到“高于正常的错误率”。&lt;/p&gt;
&lt;p&gt;截至发稿时，部分网站的访问已开始逐步恢复，但全球网络延迟和错误率仍处于高位。我们将持续关注事态的后续发展。&lt;/p&gt;</description>
        </item>
        <item>
        <title>搞懂 MySQL 间隙锁：从原理到金融级选型</title>
        <link>https://ttf248.life/p/understanding-mysql-gaps-locks-from-principles-to-enterprise-grade-selection/</link>
        <pubDate>Tue, 18 Nov 2025 22:29:25 +0800</pubDate>
        
        <guid>https://ttf248.life/p/understanding-mysql-gaps-locks-from-principles-to-enterprise-grade-selection/</guid>
        <description>&lt;p&gt;系统服务之间的数据同步，近期改动的时候，某个接口数据量上去，频繁发生死锁，同事排查后，发现是由于间隙锁导致，MySQL 用的不多，记录下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;mysql 间隙锁，背景、原理，如何关闭间隙锁、关闭了会有什么问题；你是资深数据库 DBA，针对我的问题进行扩展分析补充。幻读是什么？主流大厂是选择 RC 还是 RR模式？我是金融行业，券商系统，你更建议我用什么方案？整理上述问题，输出文档。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 MySQL 中，尤其是高并发场景下，“锁”是绕不过去的话题。间隙锁（Gap Lock）是 InnoDB 引擎中的一个核心特性，但也常常是性能瓶颈和死锁的根源。&lt;/p&gt;
&lt;p&gt;这篇文章将帮你快速梳理间隙锁的核心、利弊，并为你（特别是金融行业）提供清晰的架构选型建议。&lt;/p&gt;
&lt;h2 id=&#34;什么是间隙锁与幻读&#34;&gt;什么是间隙锁与幻读？
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;幻读 (Phantom Read)：&lt;/strong&gt;
在一个事务中，你第一次查某范围（如 &lt;code&gt;ID &amp;gt; 100&lt;/code&gt;）有 10 条数据，别人插入一条 &lt;code&gt;ID=101&lt;/code&gt; 并提交后，你第二次查，发现变成了 11 条。这条“多出来的”新数据就像幻觉，故称“幻读”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;间隙锁 (Gap Lock)：&lt;/strong&gt;
MySQL 在 &lt;strong&gt;RR（可重复读）&lt;/strong&gt; 隔离级别下，为了解决“幻读”问题而发明的。它不锁数据行，而是&lt;strong&gt;锁住数据之间的“缝隙”&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;作用：&lt;/strong&gt; 防止其他事务在这个“缝隙”中 &lt;code&gt;INSERT&lt;/code&gt; 新数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代价：&lt;/strong&gt; 锁的范围变大，并发能力下降，容易死锁。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;如何关闭间隙锁&#34;&gt;如何“关闭”间隙锁？
&lt;/h2&gt;&lt;p&gt;最直接的方法是&lt;strong&gt;将数据库隔离级别从 RR (Repeatable Read) 降为 RC (Read Committed)&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 将当前会话隔离级别设为 RC
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SET&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SESSION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;transaction_isolation&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;READ-COMMITTED&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- 永久修改（需改 my.cnf 并重启）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- [mysqld]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- transaction-isolation = READ-COMMITTED
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;-关闭的后果即-rc-级别的特性&#34;&gt;⚠️ 关闭的后果（即 RC 级别的特性）
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;性能提升：&lt;/strong&gt; 没有了间隙锁，锁的粒度更小，并发吞吐量大幅提升。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;死锁减少：&lt;/strong&gt; 绝大多数由间隙锁导致的死锁都会消失。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出现幻读/不可重复读：&lt;/strong&gt; 这是 RC 级别的“特性”，事务中两次查询的结果可能会不一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;【核心要求】必须配合 ROW 格式 Binlog：&lt;/strong&gt;
如果使用 RC 级别，&lt;strong&gt;必须&lt;/strong&gt;将 &lt;code&gt;binlog_format&lt;/code&gt; 设置为 &lt;code&gt;ROW&lt;/code&gt;。否则，在主从复制时会因为缺少间隙锁的保护而导致主从数据不一致。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;主流大厂选择rc-还是-rr&#34;&gt;主流大厂选择：RC 还是 RR？
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;答案：绝大多数一线互联网大厂都选择 RC (Read Committed) + ROW Binlog 模式。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原因：&lt;/strong&gt; 互联网业务（如电商、社交）极端追求高并发。它们无法忍受 RR 间隙锁带来的频繁锁等待和死锁。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;权衡：&lt;/strong&gt; 它们选择放弃数据库层面的“可重复读”，转而在&lt;strong&gt;应用层&lt;/strong&gt;通过乐观锁（如版本号、CAS）等方式来保证关键业务（如库存、余额）的逻辑一致性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;金融行业券商如何选择&#34;&gt;金融行业（券商）如何选择？
&lt;/h2&gt;&lt;p&gt;对于数据一致性要求极高的券商系统，建议&lt;strong&gt;分场景治理&lt;/strong&gt;：&lt;/p&gt;
&lt;h3 id=&#34;方案一核心交易系统高并发低延迟&#34;&gt;方案一：核心交易系统（高并发、低延迟）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;建议：RC (Read Committed) + ROW Binlog + 应用层乐观锁&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;场景：&lt;/strong&gt; 订单撮合、下单、资金扣减。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;理由：&lt;/strong&gt; 交易系统的并发压力不亚于互联网秒杀。RR 的间隙锁会成为性能瓶颈，导致严重的锁竞争甚至死锁，这是交易系统无法接受的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对策：&lt;/strong&gt; 使用 RC 保证高性能，同时在应用代码中（如 Java）通过 &lt;code&gt;UPDATE ... WHERE balance &amp;gt; ?&lt;/code&gt; 或 CAS 版本号机制来保证资金安全，防止超卖。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;方案二清算对账系统批处理高一致性&#34;&gt;方案二：清算对账系统（批处理、高一致性）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;建议：RR (Repeatable Read)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;场景：&lt;/strong&gt; 盘后批量对账、生成报表、日终清算。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;理由：&lt;/strong&gt; 批处理任务需要在一个“绝对一致”的数据快照上运行。它需要 RR 级别提供的能力，保证在整个统计过程中，数据不会发生“幻读”，确保总账能对平。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;隔离级别&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;优点&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;缺点&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;适用场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;RR (默认)&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;解决幻读，数据一致性高&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;并发低，易死锁 (间隙锁)&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;报表、清算、对数据一致性要求极高且并发不大的场景&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;RC&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;并发高，死锁少&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;会幻读、不可重复读&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;高并发核心业务&lt;/strong&gt; (如电商、金融交易)，需配合 ROW Binlog&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;对于券商核心系统，&lt;strong&gt;RC + ROW Binlog&lt;/strong&gt; 是兼顾性能与一致性的主流架构方案，但它要求开发团队在应用层承担更多保障数据一致性的责任。&lt;/p&gt;</description>
        </item>
        <item>
        <title>本地开发之痛：为何 `nul` 文件删不掉？一个“复合型”文件系统难题的破解之道</title>
        <link>https://ttf248.life/p/local-development-pain-why-cant-you-delete-nul-files-a-solution-to-the-composite-file-system-problem/</link>
        <pubDate>Sat, 08 Nov 2025 16:37:46 +0800</pubDate>
        
        <guid>https://ttf248.life/p/local-development-pain-why-cant-you-delete-nul-files-a-solution-to-the-composite-file-system-problem/</guid>
        <description>&lt;p&gt;在软件开发的日常工作中，我们时常会遇到一些棘手的“小问题”，它们看似简单，却能耗费我们数小时的宝贵时间。其中，在 Windows 系统上删除特定文件（尤其是那些由开发工具链意外生成的文件）无疑是“重灾区”之一。&lt;/p&gt;
&lt;p&gt;我就遇到了这样一个“地狱级”的难题：在本地开发时，项目中莫名其妙地出现了一个名为 &lt;code&gt;nul&lt;/code&gt; 的文件。我尝试了 Windows 资源管理器、CMD 命令行，但系统都提示“找不到文件”或“无法删除”。这个文件就像一个幽灵，顽固地盘踞在我的项目目录中。&lt;/p&gt;
&lt;h2 id=&#34;阶段一常规尝试与标准的无效解法&#34;&gt;阶段一：常规尝试与“标准”的无效解法
&lt;/h2&gt;&lt;p&gt;当我遇到这个问题时，我的第一反应是“&lt;code&gt;nul&lt;/code&gt; 文件”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么 &lt;code&gt;nul&lt;/code&gt; 特殊？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;熟悉 Windows 历史的开发者可能知道，&lt;code&gt;nul&lt;/code&gt; 是一个“天坑”。在 Windows (及更早的 DOS) 系统中，&lt;code&gt;NUL&lt;/code&gt;、&lt;code&gt;CON&lt;/code&gt;、&lt;code&gt;PRN&lt;/code&gt;、&lt;code&gt;AUX&lt;/code&gt; 等是保留的设备名称。&lt;code&gt;NUL&lt;/code&gt; 代表“空设备”（类似于 Unix/Linux 中的 &lt;code&gt;/dev/null&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;当 Windows 的文件系统 API 看到你试图操作一个名为 &lt;code&gt;nul&lt;/code&gt; 的“文件”时，它会认为你是在尝试操作这个“空设备”，而不是一个同名的物理文件。因此，所有常规的文件操作（如删除、重命名）都会失败。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如何产生 &lt;code&gt;nul&lt;/code&gt; 文件的？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这通常是跨平台开发工具（如 Git、Node.js 脚本、Python 脚本等）的“锅”。这些工具可能基于 POSIX (Unix-like) 标准，在它们的眼中，&lt;code&gt;nul&lt;/code&gt; 只是一个普通的文件名。它们在 Windows 上运行时，有时会绕过常规的API，直接创建出这种 Windows “消化不良”的文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;网上推荐的“标准解法”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我迅速在网上搜索，发现我不是第一个遇到这个问题的人。社区提供了几种公认的“高级”解决方案：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;使用 &lt;code&gt;\\.\&lt;/code&gt; 语法&lt;/strong&gt;：在 CMD 中使用特殊的“长路径”语法，绕过 Windows 的名称检查。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;del &lt;span class=&#34;se&#34;&gt;\\&lt;/span&gt;.&lt;span class=&#34;se&#34;&gt;\C&lt;/span&gt;:&lt;span class=&#34;se&#34;&gt;\y&lt;/span&gt;our&lt;span class=&#34;se&#34;&gt;\p&lt;/span&gt;roject&lt;span class=&#34;se&#34;&gt;\p&lt;/span&gt;ath&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;ul
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;&lt;strong&gt;使用 Git Bash&lt;/strong&gt;：Git Bash 提供了一个轻量级的 Unix 环境，它不把 &lt;code&gt;nul&lt;/code&gt; 当作特殊设备&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm nul
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;使用 WSL (Windows Subsystem for Linux)&lt;/strong&gt;：进入 WSL，挂载 Windows 磁盘，使用 Linux 的 &lt;code&gt;rm&lt;/code&gt; 命令删除。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm /mnt/c/your/project/path/nul
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然而，&lt;strong&gt;这些方法对我全都不起作用！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;无论是 WSL 还是 Git Bash，当我尝试 &lt;code&gt;rm nul&lt;/code&gt; 时，系统都报出了“No such file or directory”（没有那个文件或目录）的错误。这让我陷入了沉思，问题似乎比我想象的更复杂。&lt;/p&gt;
&lt;h2 id=&#34;阶段二灵光一现是不是多重问题叠加&#34;&gt;阶段二：灵光一现——是不是“多重问题叠加”？
&lt;/h2&gt;&lt;p&gt;如果 &lt;code&gt;nul&lt;/code&gt; 文件确实存在，为什么连 Unix 工具都说“找不到”它呢？&lt;/p&gt;
&lt;p&gt;我开始怀疑：&lt;strong&gt;是不是问题不仅出在 &lt;code&gt;nul&lt;/code&gt; 文件本身，还出在了它的“栖身之所”——它所在的目录？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我立刻打开 Git Bash（这是关键，因为 Windows 资源管理器可能不会显示异常），导航到 &lt;code&gt;nul&lt;/code&gt; 文件所在的&lt;strong&gt;上一级目录&lt;/strong&gt;，然后执行 &lt;code&gt;ls -la&lt;/code&gt; (列出所有文件，包括隐藏文件，并显示详细信息)。&lt;/p&gt;
&lt;p&gt;这时，我终于发现了“盲点”：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;那个存放 &lt;code&gt;nul&lt;/code&gt; 文件的目录，它的&lt;strong&gt;目录名本身就包含非法字符&lt;/strong&gt;！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在我的案例中，这个目录名可能是一个以空格或点（&lt;code&gt;.&lt;/code&gt;）结尾的名称，或者是包含 Windows 不允许的特殊符号（如 &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;:&lt;/code&gt;）——这些同样是开发工具在跨平台同步时“夹带的私货”。&lt;/p&gt;
&lt;p&gt;例如，一个目录在 Git Bash 中显示为 &lt;code&gt;&amp;quot;my-app &amp;quot;&lt;/code&gt; (注意末尾的空格)，或者 &lt;code&gt;&amp;quot;my-app.&amp;quot;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这就是问题所在！&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;问题A：&lt;/strong&gt; 我有一个名为 &lt;code&gt;nul&lt;/code&gt; 的“非法”文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;问题B：&lt;/strong&gt; 我有一个名为 &lt;code&gt;&amp;quot;my-app &amp;quot;&lt;/code&gt; 的“非法”目录。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当我尝试 &lt;code&gt;rm /path/to/&amp;quot;my-app &amp;quot;/nul&lt;/code&gt; 时，Windows 系统和 Unix 工具都“蒙圈”了。Windows API 无法正确解析这个包含非法字符的路径；而 Git Bash 或 WSL 虽然能“看到”这个非法目录，但在尝试访问它内部的 &lt;code&gt;nul&lt;/code&gt; 文件时，可能因为路径解析的复合问题而失败。&lt;/p&gt;
&lt;h2 id=&#34;阶段三釜底抽薪从路径下手一举歼灭&#34;&gt;阶段三：釜底抽薪——从路径下手，一举歼灭
&lt;/h2&gt;&lt;p&gt;既然确定了是“文件路径”和“文件名”的双重问题，解决方案就清晰了：&lt;strong&gt;不要试图去删除那个 &lt;code&gt;nul&lt;/code&gt; 文件，而是直接删除那个“非法”的父目录！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我的最终解决步骤如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;打开 Git Bash&lt;/strong&gt;：这是唯一能正确“看到”并处理这些非法名称的工具。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;导航到“问题目录”的上一级&lt;/strong&gt;：
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 假设问题目录是 C:\projects\my-app &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /c/projects/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;确认“问题目录”的真实名称&lt;/strong&gt;：
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ls -l
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 输出可能显示如下：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# drwxr-xr-x 1 MyUser 1049089 0 Nov 7 06:30 &amp;#39;my-app &amp;#39;/ &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 注意到了吗？ls 命令用单引号把这个带空格的目录名括起来了。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行“终极删除”&lt;/strong&gt;：使用 &lt;code&gt;rm&lt;/code&gt; 命令的 &lt;code&gt;-r&lt;/code&gt; (递归) 和 &lt;code&gt;-f&lt;/code&gt; (强制) 选项，配合&lt;strong&gt;引号&lt;/strong&gt;，来删除整个目录。
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 必须使用引号（单引号或双引号）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 并且，如果你在 Git Bash 中输入 &amp;#39;my-app &amp;#39; 然后按 Tab 键，它也能帮你自动补全&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -rf &lt;span class=&#34;s2&#34;&gt;&amp;#34;my-app &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 或者&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;rm -rf &lt;span class=&#34;s1&#34;&gt;&amp;#39;my-app &amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;执行命令后，那个困扰我许久的、包含 &lt;code&gt;nul&lt;/code&gt; 文件的、本身命名也不合法的目录，终于从我的文件系统中被彻底清除了。&lt;/p&gt;</description>
        </item>
        <item>
        <title>Keepalived&#43;HAProxy 搭建高可用负载均衡</title>
        <link>https://ttf248.life/p/keepalived-haproxy-for-high-availability-load-balancing/</link>
        <pubDate>Fri, 19 Sep 2025 09:45:55 +0800</pubDate>
        
        <guid>https://ttf248.life/p/keepalived-haproxy-for-high-availability-load-balancing/</guid>
        <description>&lt;p&gt;在现代互联网架构中，高可用性是系统设计的重要考量。本文将详细介绍如何使用Keepalived和HAProxy搭建高可用负载均衡集群，确保服务的连续性和可靠性。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;实际配置部分没有落地验证，文章正文规划依赖AI完成&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://ttf248.life/p/keepalived-haproxy-for-high-availability-load-balancing/20250919095219.png&#34;
	width=&#34;566&#34;
	height=&#34;452&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;任务规划&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;125&#34;
		data-flex-basis=&#34;300px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;技术简介&#34;&gt;技术简介
&lt;/h2&gt;&lt;h3 id=&#34;keepalived-简介&#34;&gt;Keepalived 简介
&lt;/h3&gt;&lt;p&gt;Keepalived是一个基于VRRP（Virtual Router Redundancy Protocol）协议的高可用解决方案，主要用于实现服务器的故障切换和负载均衡。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;主要特性：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VRRP协议支持&lt;/strong&gt;：实现虚拟IP的主备切换&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;健康检查&lt;/strong&gt;：监控服务状态，自动故障转移&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;配置简单&lt;/strong&gt;：通过配置文件即可实现复杂的高可用架构&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轻量级&lt;/strong&gt;：资源占用少，性能优异&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;工作原理：&lt;/strong&gt;
Keepalived通过VRRP协议在多台服务器之间共享一个虚拟IP地址。正常情况下，主服务器持有虚拟IP并提供服务；当主服务器发生故障时，备用服务器会自动接管虚拟IP，确保服务不中断。&lt;/p&gt;
&lt;h3 id=&#34;haproxy-简介&#34;&gt;HAProxy 简介
&lt;/h3&gt;&lt;p&gt;HAProxy是一个高性能的负载均衡器和反向代理服务器，广泛应用于高并发场景。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;主要功能：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;负载均衡&lt;/strong&gt;：支持多种负载均衡算法&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;健康检查&lt;/strong&gt;：实时监控后端服务器状态&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSL终结&lt;/strong&gt;：支持HTTPS流量处理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统计监控&lt;/strong&gt;：提供详细的运行状态统计&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;应用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web服务负载均衡&lt;/li&gt;
&lt;li&gt;数据库连接池&lt;/li&gt;
&lt;li&gt;微服务网关&lt;/li&gt;
&lt;li&gt;API接口代理&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;架构设计&#34;&gt;架构设计
&lt;/h2&gt;&lt;h3 id=&#34;整体架构&#34;&gt;整体架构
&lt;/h3&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;                    ┌─────────────────┐
                    │   Client        │
                    └─────────┬───────┘
                              │
                    ┌─────────▼───────┐
                    │  Virtual IP     │
                    │  (VIP)          │
                    └─────────┬───────┘
                              │
              ┌───────────────┼───────────────┐
              │               │               │
    ┌─────────▼───────┐              ┌─────────▼───────┐
    │   HAProxy-1     │              │   HAProxy-2     │
    │  (Master)       │◄────────────►│   (Backup)      │
    │  + Keepalived   │   VRRP       │  + Keepalived   │
    └─────────┬───────┘              └─────────┬───────┘
              │                                │
              └──────────┬─────────────────────┘
                         │
        ┌────────────────┼────────────────┐
        │                │                │
┌───────▼───────┐ ┌──────▼──────┐ ┌───────▼───────┐
│  Web Server 1 │ │ Web Server 2│ │  Web Server 3 │
│   Backend     │ │   Backend   │ │   Backend     │
└───────────────┘ └─────────────┘ └───────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;组件说明&#34;&gt;组件说明
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;虚拟IP (VIP)&lt;/strong&gt;：客户端访问的统一入口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HAProxy主备节点&lt;/strong&gt;：提供负载均衡服务，通过Keepalived实现高可用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后端服务器&lt;/strong&gt;：实际提供服务的Web服务器&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;环境准备&#34;&gt;环境准备
&lt;/h2&gt;&lt;h3 id=&#34;服务器规划&#34;&gt;服务器规划
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;角色&lt;/th&gt;
					&lt;th&gt;IP地址&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;HAProxy主节点&lt;/td&gt;
					&lt;td&gt;192.168.1.10&lt;/td&gt;
					&lt;td&gt;lb-master&lt;/td&gt;
					&lt;td&gt;HAProxy + Keepalived&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;HAProxy备节点&lt;/td&gt;
					&lt;td&gt;192.168.1.11&lt;/td&gt;
					&lt;td&gt;lb-backup&lt;/td&gt;
					&lt;td&gt;HAProxy + Keepalived&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;虚拟IP&lt;/td&gt;
					&lt;td&gt;192.168.1.100&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
					&lt;td&gt;VIP&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Web服务器1&lt;/td&gt;
					&lt;td&gt;192.168.1.20&lt;/td&gt;
					&lt;td&gt;web1&lt;/td&gt;
					&lt;td&gt;Nginx/Apache&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Web服务器2&lt;/td&gt;
					&lt;td&gt;192.168.1.21&lt;/td&gt;
					&lt;td&gt;web2&lt;/td&gt;
					&lt;td&gt;Nginx/Apache&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Web服务器3&lt;/td&gt;
					&lt;td&gt;192.168.1.22&lt;/td&gt;
					&lt;td&gt;web3&lt;/td&gt;
					&lt;td&gt;Nginx/Apache&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;软件安装&#34;&gt;软件安装
&lt;/h3&gt;&lt;p&gt;在HAProxy主备节点上安装必要软件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# CentOS/RHEL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;yum install -y haproxy keepalived
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Ubuntu/Debian&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apt-get install -y haproxy keepalived
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启用服务开机自启&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; haproxy keepalived
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;keepalived-配置&#34;&gt;Keepalived 配置
&lt;/h2&gt;&lt;h3 id=&#34;主节点配置-lb-master&#34;&gt;主节点配置 (lb-master)
&lt;/h3&gt;&lt;p&gt;创建配置文件 &lt;code&gt;/etc/keepalived/keepalived.conf&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;! Configuration File &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; keepalived
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;global_defs &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    router_id LB_MASTER
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    script_user root
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    enable_script_security
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查HAProxy服务状态的脚本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vrrp_script chk_haproxy &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    script &lt;span class=&#34;s2&#34;&gt;&amp;#34;/etc/keepalived/check_haproxy.sh&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    interval &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    weight -2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    fall &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    rise &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vrrp_instance VI_1 &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    state MASTER
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    interface eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    virtual_router_id &lt;span class=&#34;m&#34;&gt;51&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    priority &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    advert_int &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    authentication &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        auth_type PASS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        auth_pass mypassword123
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    virtual_ipaddress &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        192.168.1.100/24
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    track_script &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        chk_haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    notify_master &lt;span class=&#34;s2&#34;&gt;&amp;#34;/etc/keepalived/notify.sh master&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    notify_backup &lt;span class=&#34;s2&#34;&gt;&amp;#34;/etc/keepalived/notify.sh backup&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    notify_fault &lt;span class=&#34;s2&#34;&gt;&amp;#34;/etc/keepalived/notify.sh fault&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;备节点配置-lb-backup&#34;&gt;备节点配置 (lb-backup)
&lt;/h3&gt;&lt;p&gt;创建配置文件 &lt;code&gt;/etc/keepalived/keepalived.conf&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;! Configuration File &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; keepalived
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;global_defs &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    router_id LB_BACKUP
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    script_user root
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    enable_script_security
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vrrp_script chk_haproxy &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    script &lt;span class=&#34;s2&#34;&gt;&amp;#34;/etc/keepalived/check_haproxy.sh&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    interval &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    weight -2
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    fall &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    rise &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;vrrp_instance VI_1 &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    state BACKUP
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    interface eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    virtual_router_id &lt;span class=&#34;m&#34;&gt;51&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    priority &lt;span class=&#34;m&#34;&gt;90&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    advert_int &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    authentication &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        auth_type PASS
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        auth_pass mypassword123
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    virtual_ipaddress &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        192.168.1.100/24
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    track_script &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        chk_haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    notify_master &lt;span class=&#34;s2&#34;&gt;&amp;#34;/etc/keepalived/notify.sh master&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    notify_backup &lt;span class=&#34;s2&#34;&gt;&amp;#34;/etc/keepalived/notify.sh backup&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    notify_fault &lt;span class=&#34;s2&#34;&gt;&amp;#34;/etc/keepalived/notify.sh fault&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;健康检查脚本&#34;&gt;健康检查脚本
&lt;/h3&gt;&lt;p&gt;创建HAProxy健康检查脚本 &lt;code&gt;/etc/keepalived/check_haproxy.sh&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查HAProxy进程是否运行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ps -C haproxy --no-header &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; wc -l&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -eq &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 尝试启动HAProxy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    systemctl start haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    sleep &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# 再次检查，如果还是没有运行则退出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;ps -C haproxy --no-header &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; wc -l&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt; -eq &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查HAProxy端口是否监听&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; ! netstat -tuln &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep -q &lt;span class=&#34;s2&#34;&gt;&amp;#34;:80 &amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;exit&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;状态通知脚本&#34;&gt;状态通知脚本
&lt;/h3&gt;&lt;p&gt;创建状态通知脚本 &lt;code&gt;/etc/keepalived/notify.sh&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;TYPE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;NAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;STATE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$STATE&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;MASTER&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;: Became MASTER&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /var/log/keepalived-state.log
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;BACKUP&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;: Became BACKUP&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /var/log/keepalived-state.log
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;FAULT&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;: Fault detected&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /var/log/keepalived-state.log
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    *&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;$(&lt;/span&gt;date&lt;span class=&#34;k&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;: Unknown state: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$STATE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /var/log/keepalived-state.log
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;esac&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=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod +x /etc/keepalived/check_haproxy.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;chmod +x /etc/keepalived/notify.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;haproxy-配置&#34;&gt;HAProxy 配置
&lt;/h2&gt;&lt;h3 id=&#34;主配置文件&#34;&gt;主配置文件
&lt;/h3&gt;&lt;p&gt;在主备节点上创建相同的HAProxy配置文件 &lt;code&gt;/etc/haproxy/haproxy.cfg&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;global
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    log 127.0.0.1:514 local0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    chroot /var/lib/haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    stats socket /run/haproxy/admin.sock mode &lt;span class=&#34;m&#34;&gt;660&lt;/span&gt; level admin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    stats timeout 30s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    user haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    group haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    daemon
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;defaults
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    mode http
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    log global
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    option httplog
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    option dontlognull
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    option log-health-checks
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    option forwardfor except 127.0.0.0/8
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    option redispatch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    retries &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    timeout http-request 10s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    timeout queue 1m
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    timeout connect 10s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    timeout client 1m
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    timeout server 1m
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    timeout http-keep-alive 10s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    timeout check 10s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    maxconn &lt;span class=&#34;m&#34;&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 统计页面配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;listen stats
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;bind&lt;/span&gt; *:8080
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    stats &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    stats uri /stats
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    stats realm HAProxy&lt;span class=&#34;se&#34;&gt;\ &lt;/span&gt;Statistics
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    stats auth admin:password123
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    stats refresh 30s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 前端配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;frontend web_frontend
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;bind&lt;/span&gt; *:80
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    default_backend web_servers
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 后端服务器配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;backend web_servers
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    balance roundrobin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    option httpchk GET /health
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    server web1 192.168.1.20:80 check inter &lt;span class=&#34;m&#34;&gt;2000&lt;/span&gt; rise &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; fall &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    server web2 192.168.1.21:80 check inter &lt;span class=&#34;m&#34;&gt;2000&lt;/span&gt; rise &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; fall &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    server web3 192.168.1.22:80 check inter &lt;span class=&#34;m&#34;&gt;2000&lt;/span&gt; rise &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; fall &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;配置说明&#34;&gt;配置说明
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;全局配置：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;log&lt;/code&gt;：日志配置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chroot&lt;/code&gt;：安全沙箱&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stats socket&lt;/code&gt;：管理接口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;daemon&lt;/code&gt;：后台运行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;默认配置：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mode http&lt;/code&gt;：HTTP模式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;balance roundrobin&lt;/code&gt;：轮询负载均衡&lt;/li&gt;
&lt;li&gt;&lt;code&gt;option httpchk&lt;/code&gt;：HTTP健康检查&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timeout&lt;/code&gt;：各种超时设置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;后端服务器：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;check&lt;/code&gt;：启用健康检查&lt;/li&gt;
&lt;li&gt;&lt;code&gt;inter 2000&lt;/code&gt;：检查间隔2秒&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rise 2&lt;/code&gt;：连续2次成功标记为可用&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fall 3&lt;/code&gt;：连续3次失败标记为不可用&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;服务启动与测试&#34;&gt;服务启动与测试
&lt;/h2&gt;&lt;h3 id=&#34;启动服务&#34;&gt;启动服务
&lt;/h3&gt;&lt;p&gt;在主备节点上启动服务：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启动HAProxy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl start haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl status haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启动Keepalived&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl start keepalived
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl status keepalived
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;验证vip绑定&#34;&gt;验证VIP绑定
&lt;/h3&gt;&lt;p&gt;检查虚拟IP是否正确绑定：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在主节点查看IP地址&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip addr show
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 应该能看到类似输出：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# eth0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc pfifo_fast state UP group default qlen 1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#     inet 192.168.1.10/24 brd 192.168.1.255 scope global eth0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#     inet 192.168.1.100/24 scope global secondary eth0:0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;功能测试&#34;&gt;功能测试
&lt;/h3&gt;&lt;h4 id=&#34;1-负载均衡测试&#34;&gt;1. 负载均衡测试
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 多次访问VIP，观察请求分发&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; i in &lt;span class=&#34;o&#34;&gt;{&lt;/span&gt;1..10&lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    curl -s http://192.168.1.100/ &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;Server&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;2-故障切换测试&#34;&gt;2. 故障切换测试
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在主节点停止HAProxy服务&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl stop haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 观察VIP是否切换到备节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ip addr show
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 测试服务是否正常&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl http://192.168.1.100/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;3-后端服务器故障测试&#34;&gt;3. 后端服务器故障测试
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 停止其中一台Web服务器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在web1服务器上：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;systemctl stop nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 观察HAProxy统计页面&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl http://192.168.1.100:8080/stats
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;监控与维护&#34;&gt;监控与维护
&lt;/h2&gt;&lt;h3 id=&#34;日志监控&#34;&gt;日志监控
&lt;/h3&gt;&lt;h4 id=&#34;haproxy日志&#34;&gt;HAProxy日志
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看HAProxy日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tail -f /var/log/haproxy.log
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看访问统计&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;HTTP/1.1&amp;#34;&lt;/span&gt; /var/log/haproxy.log &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; tail -20
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;keepalived日志&#34;&gt;Keepalived日志
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看Keepalived日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tail -f /var/log/messages &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep keepalived
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看状态变化日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tail -f /var/log/keepalived-state.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;性能监控&#34;&gt;性能监控
&lt;/h3&gt;&lt;h4 id=&#34;统计页面监控&#34;&gt;统计页面监控
&lt;/h4&gt;&lt;p&gt;访问HAProxy统计页面：&lt;code&gt;http://192.168.1.100:8080/stats&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;关键指标：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Session Rate&lt;/strong&gt;：会话速率&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session Total&lt;/strong&gt;：总会话数&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bytes In/Out&lt;/strong&gt;：流量统计&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Response Time&lt;/strong&gt;：响应时间&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Status&lt;/strong&gt;：服务器状态&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;命令行监控&#34;&gt;命令行监控
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看HAProxy进程状态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ps aux &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep haproxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看端口监听状态&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;netstat -tuln &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep -E &lt;span class=&#34;s2&#34;&gt;&amp;#34;(80|8080)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看连接数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ss -ant &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep :80 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; wc -l
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;常见问题排查&#34;&gt;常见问题排查
&lt;/h2&gt;&lt;h3 id=&#34;1-vip无法切换&#34;&gt;1. VIP无法切换
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题现象：&lt;/strong&gt;
主节点故障后，VIP没有切换到备节点&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查Keepalived配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;keepalived -t -f /etc/keepalived/keepalived.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看VRRP通信&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;tcpdump -i eth0 vrrp
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 检查防火墙设置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;iptables -L &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep vrrp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确保VRRP协议通信正常&lt;/li&gt;
&lt;li&gt;检查网络接口配置&lt;/li&gt;
&lt;li&gt;验证认证密码一致性&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-健康检查失败&#34;&gt;2. 健康检查失败
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题现象：&lt;/strong&gt;
后端服务器被标记为不可用&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 手动执行健康检查&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -I http://192.168.1.20/health
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看HAProxy日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;grep &lt;span class=&#34;s2&#34;&gt;&amp;#34;Health check&amp;#34;&lt;/span&gt; /var/log/haproxy.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确保健康检查URL可访问&lt;/li&gt;
&lt;li&gt;调整检查间隔和阈值&lt;/li&gt;
&lt;li&gt;检查后端服务器状态&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-负载不均衡&#34;&gt;3. 负载不均衡
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;问题现象：&lt;/strong&gt;
请求没有均匀分发到后端服务器&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查步骤：&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 查看统计页面&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -s http://192.168.1.100:8080/stats
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 分析访问日志&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;{print $6}&amp;#39;&lt;/span&gt; /var/log/haproxy.log &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; sort &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; uniq -c
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;解决方案：&lt;/strong&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;/ul&gt;
&lt;h2 id=&#34;优化建议&#34;&gt;优化建议
&lt;/h2&gt;&lt;h3 id=&#34;1-性能优化&#34;&gt;1. 性能优化
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 调整系统参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;net.core.somaxconn = 65535&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;net.ipv4.tcp_max_syn_backlog = 65535&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sysctl -p
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 优化HAProxy配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 增加maxconn值&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 调整timeout参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启用压缩功能&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-安全加固&#34;&gt;2. 安全加固
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 限制统计页面访问&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 在haproxy.cfg中添加ACL规则&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;acl allowed_ips src 192.168.1.0/24
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;http-request deny &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; !allowed_ips
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 启用SSL/TLS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;bind&lt;/span&gt; *:443 ssl crt /etc/ssl/certs/server.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;redirect scheme https &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; !&lt;span class=&#34;o&#34;&gt;{&lt;/span&gt; ssl_fc &lt;span class=&#34;o&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-监控告警&#34;&gt;3. 监控告警
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 集成监控系统&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 配置Prometheus监控&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 设置Grafana仪表板&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 配置告警规则&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;总结&#34;&gt;总结
&lt;/h2&gt;&lt;p&gt;通过Keepalived和HAProxy的组合，我们成功构建了一个高可用的负载均衡集群。这个方案具有以下优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;高可用性&lt;/strong&gt;：通过VRRP协议实现自动故障切换&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;负载均衡&lt;/strong&gt;：智能分发请求，提高系统性能&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;健康检查&lt;/strong&gt;：实时监控服务状态，自动剔除故障节点&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;易于维护&lt;/strong&gt;：配置简单，管理方便&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;成本效益&lt;/strong&gt;：使用开源软件，降低运维成本&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在生产环境中部署时，还需要考虑网络安全、监控告警、备份恢复等方面的完善，以确保系统的稳定可靠运行。&lt;/p&gt;</description>
        </item>
        
    </channel>
</rss>
