<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>AI Computer on Uncle Xiang&#39;s Notebook</title>
        <link>https://ttf248.life/en/categories/ai-computer/</link>
        <description>Recent content in AI Computer on Uncle Xiang&#39;s Notebook</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Sun, 26 Jul 2026 22:50:29 +0800</lastBuildDate><atom:link href="https://ttf248.life/en/categories/ai-computer/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Vera Rubin&#39;s 10x Tokens per Megawatt — Will It Bring Down AI Prices?</title>
        <link>https://ttf248.life/en/p/vera-rubin-tokens-per-megawatt-ai-price/</link>
        <pubDate>Wed, 22 Jul 2026 21:13:54 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/vera-rubin-tokens-per-megawatt-ai-price/</guid>
        <description>&lt;p&gt;According to the vendor benchmarks published by NVIDIA for DeepSeek-R1 inference, the Vera Rubin NVL72 delivers ten times the tokens-per-megawatt metric compared to its GB200 NVL72 counterpart. This is a rack-level system comparison, not a single-GPU conclusion.&lt;/p&gt;
&lt;p&gt;This number is large enough to lead one to the next conclusion: since the same megawatt can do ten times the work, AI should become cheaper soon.&lt;/p&gt;
&lt;p&gt;I think the general direction is right, but the conclusion was reached too quickly.&lt;/p&gt;
&lt;p&gt;First, put this &amp;ldquo;10x&amp;rdquo; back into its testing context. It is a vendor benchmark by NVIDIA measuring DeepSeek-R1 inference workloads, rack-scale NVL72 versus rack-scale NVL72. It states how many tokens can be delivered per megawatt in power-constrained scenarios; it does not claim all models are 10x faster, nor that the total cost per million tokens has dropped by 90%. The publicly available materials are insufficient to convert this metric into real-world serving costs: training, long context, low concurrency, small batches, different precisions, and different service-quality targets could each produce a different picture.&lt;/p&gt;
&lt;p&gt;However, this metric still matters. In projects constrained by power access, liquid cooling, or data center delivery, if the same power quota can run more inference, expansion does not have to wait for substations and data centers every time.&lt;/p&gt;
&lt;p&gt;The problem is that electricity is just one line item on a cost sheet.&lt;/p&gt;
&lt;p&gt;A Vera Rubin NVL72 rack isn&amp;rsquo;t just a matter of swapping out cards in older racks. New GPUs, interconnects, switching fabrics, liquid cooling, and deployment engineering all enter the capital expenditure together. Service providers also have to face depreciation cycles, financing costs, software migration, fault tolerance, and utilization. When machines sit idle, no matter how impressive the per-megawatt throughput looks, it doesn&amp;rsquo;t automatically translate into cheaper Tokens.&lt;/p&gt;
&lt;p&gt;Simplify the per-unit service cost of the service provider as:&lt;/p&gt;
$$
\text{Unit Token Cost} \approx \frac{\text{Depreciation + Electricity + Data Center Network + Operations \&amp; Maintenance}}{\text{Actually Sold Tokens}}
$$&lt;p&gt;This is a simplified framework for explaining variable relationships, not a TCO calculated from public materials; the denominator here is the actual Tokens sold, not the device&amp;rsquo;s theoretical throughput. The per-megawatt Tokens baseline directly improves the inference capacity that can be supported per unit of electricity, and may reduce the electricity input per Token; the unit service cost improves through higher actual Tokens sold only when demand and utilization keep pace. The new system&amp;rsquo;s capital expenditure, depreciation, and deployment costs may also rise. Networking, storage, queuing, and scheduling can likewise become limiting factors — the system doesn&amp;rsquo;t run on a single GPU.&lt;/p&gt;
&lt;p&gt;What happens first is not necessarily a price reduction. Service providers may first use the new capacity for longer context, faster response, higher concurrency, or to handle enterprise demands that were previously queued up; this is valuable for developers, yet the price list does not have to change immediately.&lt;/p&gt;
&lt;p&gt;The real price drops will only come once new racks evolve from pilot projects into repeatable configurations in actual data center facilities, and once utilization is meaningfully driven up. Until then, whether cloud providers, model companies, and inference service providers are willing to pass efficiency gains on to customers will depend on competition. The more intractable problem lies on the demand side: if cheap inference immediately triggers a tenfold increase in call volume, the industry might end up with even greater aggregate spending and more data centers, rather than everyone feeling things are cheaper.&lt;/p&gt;
&lt;p&gt;AI infrastructure produces both deflation and expansion at the same time. A single generation may use less power, and users may get better model capabilities, yet providers may end up building more data centers as a result, because new capabilities pull forth new use cases.&lt;/p&gt;
&lt;p&gt;Therefore, Vera Rubin is ten times more like a ticket toward price reductions than a notice of price reductions. It improves inference capacity in power-constrained scenarios and leaves room for service providers to cut prices. Who ultimately captures this room depends on hardware pricing, depreciation, data center operations, utilization rates, and competition.&lt;/p&gt;
&lt;p&gt;End users do not ultimately need to fixate on whether &amp;ldquo;APIs will immediately become ten times cheaper.&amp;rdquo; Two more down-to-earth signals are worth watching instead: whether the same budget can stably buy faster, longer, and more reliable inference, and once supply ramps up, how much of the efficiency providers will retain on their profit-and-loss statement versus how much they will pass through into the pricing sheet. The former may first manifest itself in the form of product capabilities; whether the latter occurs, and when, still depends on supply, utilization, and competition.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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 Data Center Platform&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 Networking&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;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;NVIDIA Vera Rubin has entered full-scale mass production, with Spectrum-6 networking and NVL72 energy efficiency improving by 10x. DeepSeek R1 inference testing shows that Vera Rubin NVL72 delivers a 10x increase in tokens throughput per megawatt compared to GB200 NVL72. Once widely deployed, will AI costs decrease? The energy efficiency improvement is evident, but hardware investment has also increased significantly.&lt;/p&gt;
&lt;/blockquote&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Feed several code anchors to the encoding model</title>
        <link>https://ttf248.life/en/p/code-anchors-for-llm-requirements/</link>
        <pubDate>Sun, 05 Jul 2026 23:04:15 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/code-anchors-for-llm-requirements/</guid>
        <description>&lt;p&gt;When writing requirements for coding models, I increasingly tend to include a few names that can be searched in the repository: function names, component names, CSS class names, or interface paths. Without these terms, the model may still be able to complete the task, but it often has to spend an extra round guessing &amp;ldquo;which piece of code does this sentence correspond to.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;For example, just write:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Adjust the disabled state of the login button, prevent repeated clicks while submitting, and also gray out the style.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A human can identify where the &amp;ldquo;login button&amp;rdquo; is based on the visual impression of a page, but a coding model may be confronted with dozens of buttons, multiple login entry points, and implementations scattered across components, state management, and style files. It must first map the phrase &amp;ldquo;login button&amp;rdquo; from natural language to the specific symbols in the repository, and only then can it determine whether to modify the event handler, state variables, or CSS.&lt;/p&gt;
&lt;p&gt;If the requirements are modified as follows, the search space will be much smaller:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Adjust the submission state of &lt;code&gt;handleSubmit&lt;/code&gt; in &lt;code&gt;LoginForm&lt;/code&gt;. Disable &lt;code&gt;.login-submit&lt;/code&gt; during submission to prevent duplicate requests; the existing error notification behavior remains unchanged. Acceptance: clicking again before the request completes will not send a second request, the button appears in a disabled style, and it is restored after the request finishes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What&amp;rsquo;s truly useful here is not that the prompts become longer, but that there are a few more anchors that can be landed on in code.&lt;/p&gt;
&lt;h2 id=&#34;the-model-is-making-predictions-but-the-agent-still-needs-to-find-the-code-first&#34;&gt;The Model Is Making Predictions, But the Agent Still Needs to Find the Code First
&lt;/h2&gt;&lt;p&gt;Current mainstream large language models typically split input into tokens and progressively predict subsequent tokens based on existing context. Transformer&amp;rsquo;s self-attention mechanism allows tokens in a sequence to establish connections with each other; after large-scale pre-training and subsequent alignment, the model can follow instructions, explain code, and generate modification plans. However, &amp;ldquo;being able to generate based on context&amp;rdquo; is not the same as &amp;ldquo;naturally knowing where the login button is placed in your current repository.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The coding agent has an additional layer of work: it invokes file search, text retrieval, read, and test tools to bring the relevant code from the repository into the model&amp;rsquo;s context. At this stage, the function name &lt;code&gt;handleSubmit&lt;/code&gt; and the class name &lt;code&gt;.login-submit&lt;/code&gt; serve a dual purpose.&lt;/p&gt;
&lt;p&gt;The first layer is &lt;strong&gt;retrieval anchors&lt;/strong&gt;. A phrase like &amp;ldquo;login button&amp;rdquo; in natural language can match copy, comments, tests, and multiple components; precise symbols can be handed directly to text-search tools to quickly locate definitions, references, and adjacent tests. Reading fewer irrelevant files not only saves time and tokens, but also reduces the chance of unrelated code cluttering your judgment.&lt;/p&gt;
&lt;p&gt;The second layer is &lt;strong&gt;semantic constraints&lt;/strong&gt;. &lt;code&gt;handleSubmit&lt;/code&gt; suggests that the change is related to the submission flow, &lt;code&gt;.login-submit&lt;/code&gt; narrows the visual state to a specific selector, and &lt;code&gt;LoginForm&lt;/code&gt; further provides the component boundary. Together, they reduce the ambiguity around &amp;ldquo;which button exactly, and which layer should hold the state.&amp;rdquo; The model is still doing probabilistic generation, but there are fewer plausible interpretations, making it easier for the next step to unfold along the correct code path.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s also why I&amp;rsquo;d rather call this approach &amp;ldquo;providing coordinates&amp;rdquo; than a &amp;ldquo;prompting trick.&amp;rdquo; It helps both the model and the toolchain around the model. Even if you switch to a different coding model, as long as it also needs to search the repository, these coordinates remain valuable.&lt;/p&gt;
&lt;h2 id=&#34;a-good-requirement-is-more-than-just-a-string-of-keywords&#34;&gt;A Good Requirement Is More Than Just a String of Keywords
&lt;/h2&gt;&lt;p&gt;Having only the symbol name is not enough. Although the following writing style is easy to search for, it still doesn&amp;rsquo;t tell you what to change it to:&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;, optimize it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The format I use more often now includes four categories of information:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Information&lt;/th&gt;
					&lt;th&gt;Purpose&lt;/th&gt;
					&lt;th&gt;Example&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Code Anchors&lt;/td&gt;
					&lt;td&gt;Narrow down the search scope&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;Target Behavior&lt;/td&gt;
					&lt;td&gt;Explain what happens after the change&lt;/td&gt;
					&lt;td&gt;Prevent duplicate requests during submission&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Preservation Items&lt;/td&gt;
					&lt;td&gt;Prevent accidental changes to adjacent behaviors&lt;/td&gt;
					&lt;td&gt;Existing error messages remain unchanged&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Acceptance Criteria&lt;/td&gt;
					&lt;td&gt;Provide an endpoint for implementation and testing&lt;/td&gt;
					&lt;td&gt;The second click does not send a request, and the request state is restored after it ends&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;File paths, test names, interface names, and error message text can also serve as anchors. Prioritize names you have personally verified and that are highly recognizable; you don&amp;rsquo;t need to pile in every related symbol just to appear specific. Usually one or two entry functions, a component, or a style name is already enough to let the agent start searching; the remaining call relationships should be verified by it from the code, rather than being filled in from memory by the requirement writer.&lt;/p&gt;
&lt;p&gt;Also be wary of incorrect coordinates. When functions have been renamed, class names are reused in multiple places, or the requirement actually points to a different page, precise yet wrong keywords will lead the model astray faster. A safer approach is to add a sentence like &amp;ldquo;the name may have changed, please search to confirm first,&amp;rdquo; or provide both the page behavior and visible text so the agent can cross-verify.&lt;/p&gt;
&lt;p&gt;So the more practical conclusion is not &amp;ldquo;stuff more keywords into the requirements,&amp;rdquo; but rather: &lt;strong&gt;translate the human&amp;rsquo;s impression of the page into coordinates that the repository can search, then constrain modification outcomes with behavior and acceptance criteria.&lt;/strong&gt; The former reduces the cost of finding code; the latter reduces the room for writing incorrect code. When both parts are present simultaneously, the efficiency gains of coding models become more stable.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;When writing requirements with large models, if you can include some keywords, such as: associated function names, associated frontend style names, it can make the model more efficient. Briefly explain the principles of LLM, explain the principles of current large models, and explain why what I do is better.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This piece preserves the on-site judgment that &amp;ldquo;function names and style names can boost coding efficiency,&amp;rdquo; and adds the distinction between language model generation and coding agent retrieval. It trims out full Transformer tutorials, model generation comparisons, and generalization prompt checklists, preventing mechanism introductions from overshadowing the main thread of engineering questions.&lt;/p&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Hermes Search Unstable: Outbound Traffic Handled by Mihomo</title>
        <link>https://ttf248.life/en/p/hermes-searxng-mihomo-proxy/</link>
        <pubDate>Sun, 21 Jun 2026 20:33:01 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/hermes-searxng-mihomo-proxy/</guid>
        <description>&lt;p&gt;After Hermes is connected to SearXNG, it may appear on the surface that the search problem has been brought back to the local machine: &lt;code&gt;SEARXNG_URL=http://localhost:8888&lt;/code&gt;. However, the place where servers located in mainland China are most likely to get stuck is not the hop from Hermes to SearXNG, but rather the fact that SearXNG itself still needs to access search sources such as Google, DuckDuckGo, Brave, and Startpage.&lt;/p&gt;
&lt;p&gt;I ended up splitting the chain into three layers: Hermes only accesses the local SearXNG; SearXNG is explicitly configured to delegate outbound HTTP/HTTPS requests to Mihomo; Mihomo alone handles subscriptions, health checks, and automatic node selection. The benefit of doing this is not that &amp;ldquo;the configuration is cooler,&amp;rdquo; but that when something goes wrong, you can troubleshoot it layer by layer: whether Hermes has requested local search, whether SearXNG has returned JSON, whether Mihomo has available nodes, and whether the subscription itself has failed to parse.&lt;/p&gt;
&lt;p&gt;The overall pipeline is as follows:&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 auto-selects available nodes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -&amp;gt; Overseas search engine
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Only the local port is exposed on the host machine:&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, used for debugging
&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, used for local management
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since SearXNG and Mihomo are on the same Docker network, SearXNG does not access the host&amp;rsquo;s &lt;code&gt;127.0.0.1:7897&lt;/code&gt;, but the container name:&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;the-two-most-easily-missed-places-in-searxng&#34;&gt;The Two Most Easily Missed Places in SearXNG
&lt;/h2&gt;&lt;p&gt;When Hermes calls SearXNG, it requests JSON. SearXNG&amp;rsquo;s official Search API documentation also makes it very clear: whether the &lt;code&gt;format&lt;/code&gt; parameter can be used depends on whether the corresponding format is enabled under &lt;code&gt;search.formats&lt;/code&gt; in &lt;code&gt;settings.yml&lt;/code&gt;; requesting a format that is not enabled will return a 403. Therefore, you can&amp;rsquo;t keep just the default HTML here:&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;The second issue is proxies. Do not expect Docker environment variables or system-level &lt;code&gt;http_proxy&lt;/code&gt; to be reliably inherited by the application layer. SearXNG initiates its own requests, so the most straightforward approach is to specify them explicitly in &lt;code&gt;outgoing.proxies&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# all outgoing requests are forwarded to this proxy
#  SearXNG 所发出的所有出站请求都将通过此代理转发
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;正如你看到的，这个文件里 &lt;code&gt;proxy&lt;/code&gt; 字段就在旁边就是注释&lt;code&gt;# SearXNG 所发出的所有出站请求都将通过此代理转发&lt;/code&gt;，用了 &lt;code&gt;#&lt;/code&gt; 而 Hugo shortcode 不一样，所以：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;outgoing.proxies:
  http://:  # 注释掉了
&lt;/code&gt;&lt;/pre&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;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;In the default SearXNG configuration examples, you can see the &lt;code&gt;all://:&lt;/code&gt; syntax, so it isn&amp;rsquo;t inherently an incorrect configuration option. However, in the environment I used this time, &lt;code&gt;all://:&lt;/code&gt; did not match as expected, and what ended up working reliably was writing &lt;code&gt;http://&lt;/code&gt; and &lt;code&gt;https://&lt;/code&gt; separately. I&amp;rsquo;m keeping this boundary in the article to avoid turning a single real-world test into a definitive conclusion about all versions.&lt;/p&gt;
&lt;p&gt;The complete SearXNG key configuration can be compressed as follows:&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;please replace with a random secret key&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;Here is a small gotcha: in SearXNG&amp;rsquo;s default engine configuration, the &lt;code&gt;name&lt;/code&gt; for Bing News is &lt;code&gt;bing news&lt;/code&gt;, while &lt;code&gt;engine&lt;/code&gt; is &lt;code&gt;bing_news&lt;/code&gt;. When &lt;code&gt;use_default_settings: true&lt;/code&gt; is set, &lt;code&gt;engines&lt;/code&gt; are merged and overridden by &lt;code&gt;name&lt;/code&gt;, so here you need to write &lt;code&gt;name: bing news&lt;/code&gt;, not &lt;code&gt;name: bing_news&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;mihomo-does-only-one-thing-provide-searxng-with-a-stable-egress&#34;&gt;Mihomo does only one thing: provide SearXNG with a stable egress
&lt;/h2&gt;&lt;p&gt;Mihomo does not need to take over the entire server here, nor does it require Docker to route all traffic through a proxy. It only opens a mixed port, allowing SearXNG within the same Docker network to access it:&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;Please replace with a random key&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;Your subscription URL&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;This configuration has a narrow meaning: the subscription updates every 3600 seconds, nodes perform a health check every 300 seconds, &lt;code&gt;AUTO&lt;/code&gt; uses &lt;code&gt;url-test&lt;/code&gt; to select nodes based on latency, and all traffic ultimately matches &lt;code&gt;PROXY&lt;/code&gt;. When the new configuration starts for the first time, the first item in the &lt;code&gt;PROXY&lt;/code&gt; list is &lt;code&gt;AUTO&lt;/code&gt;; if you have manually switched nodes in the control panel later, &lt;code&gt;store-selected: true&lt;/code&gt; will preserve the selection, and at this point &amp;ldquo;default to AUTO&amp;rdquo; is not necessarily true.&lt;/p&gt;
&lt;p&gt;Also pay attention to the subscription format. &lt;code&gt;proxy-providers&lt;/code&gt; works with the provider format or subscriptions that Mihomo can parse as a provider; some services deliver a full Clash configuration, while others provide a node list. If the logs report a parsing failure, don&amp;rsquo;t blame SearXNG first—go to the service provider&amp;rsquo;s dashboard and switch to the &lt;code&gt;Clash Meta&lt;/code&gt;, &lt;code&gt;Mihomo&lt;/code&gt;, or &lt;code&gt;Proxy Provider&lt;/code&gt; format.&lt;/p&gt;
&lt;h2 id=&#34;refactoring-scripts&#34;&gt;Refactoring Scripts
&lt;/h2&gt;&lt;p&gt;The following script is intended for machines where SearXNG is already placed in &lt;code&gt;/opt/searxng&lt;/code&gt;. It backs up the existing &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;searxng/settings.yml&lt;/code&gt;, reuses the local &lt;code&gt;metacubex/mihomo&lt;/code&gt; image, and puts SearXNG, Valkey, and Mihomo into the same compose project.&lt;/p&gt;
&lt;p&gt;The script does not actively pull the Mihomo image; whether SearXNG and Valkey can be started with &lt;code&gt;docker compose up&lt;/code&gt; when the corresponding images are not available locally depends on your local images and network environment. In a fully offline environment, prepare all three images in advance.&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&lt;/span&gt;_FILE&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$APP_DIR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/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;nv&#34;&gt;SETTINGS_FILE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$APP_DIR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/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;nv&#34;&gt;MIHOMO_DIR&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$APP_DIR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/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&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; ! -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&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;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Cannot find &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;. Please confirm that SearXNG is deployed at /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;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;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; ! -f &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$SETTINGS_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&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;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Cannot find &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$SETTINGS_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;. Please confirm the path of SearXNG&amp;#39;s 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;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;k&#34;&gt;if&lt;/span&gt; docker image inspect metacubex/mihomo:latest &amp;gt;/dev/null 2&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;1&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;nv&#34;&gt;MIHOMO_IMAGE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&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;k&#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;nv&#34;&gt;MIHOMO_IMAGE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&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;docker images --format &lt;span class=&#34;s1&#34;&gt;&amp;#39;{{.Repository}}:{{.Tag}}&amp;#39;&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; awk -F: &lt;span class=&#34;s1&#34;&gt;&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;&lt;/span&gt;&lt;span class=&#34;k&#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 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;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -z &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;nv&#34;&gt;MIHOMO_IMAGE&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;:-&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 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;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;No usable local metacubex/mihomo image detected.&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;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Please check first: 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;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;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Detected local Mihomo image: &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MIHOMO_IMAGE&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 class=&#34;nv&#34;&gt;LOCAL_MIHOMO_IMAGE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&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;docker tag &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MIHOMO_IMAGE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$LOCAL_MIHOMO_IMAGE&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 class=&#34;nb&#34;&gt;printf&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Please enter your Clash/Mihomo subscription URL: &amp;#34;&lt;/span&gt; &amp;gt; /dev/tty
&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;IFS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt; -r -s SUB_URL &amp;lt; /dev/tty
&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;printf&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;\n&amp;#34;&lt;/span&gt; &amp;gt; /dev/tty
&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;if&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt; -z &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$SUB_URL&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&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;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Subscription URL is empty. Aborting.&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;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;mkdir -p &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MIHOMO_DIR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/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;chmod &lt;span class=&#34;m&#34;&gt;700&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MIHOMO_DIR&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 class=&#34;nv&#34;&gt;TS&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&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 +%Y%m%d-%H%M%S&lt;span class=&#34;k&#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;cp -a &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.bak.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TS&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;cp -a &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$SETTINGS_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$SETTINGS_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.bak.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TS&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 class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Backups created:&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;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;  &lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$COMPOSE_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.bak.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TS&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 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;nv&#34;&gt;$SETTINGS_FILE&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.bak.&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$TS&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 class=&#34;nv&#34;&gt;MIHOMO_SECRET&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&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;openssl rand -hex 16&lt;span class=&#34;k&#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;cat &amp;gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$MIHOMO_DIR&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;/config.yaml&amp;#34;&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&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&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;ch &lt;span class=&#34;sb&#34;&gt;```&lt;/span&gt;yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - searxng-cache:/var/cache/searxng:rw
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    depends_on:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - valkey
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - mihomo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - searxng-net
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    logging:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      driver: json-file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      options:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        max-size: &lt;span class=&#34;s2&#34;&gt;&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;        max-file: &lt;span class=&#34;s2&#34;&gt;&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&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  valkey:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    image: docker.io/valkey/valkey:8-alpine
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    container_name: searxng-valkey
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    restart: unless-stopped
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    command: valkey-server --save &lt;span class=&#34;m&#34;&gt;30&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; --loglevel warning
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - valkey-data:/data
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - searxng-net
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    logging:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      driver: json-file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      options:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        max-size: &lt;span class=&#34;s2&#34;&gt;&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;        max-file: &lt;span class=&#34;s2&#34;&gt;&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&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  mihomo:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    image: &lt;span class=&#34;nv&#34;&gt;$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;    container_name: searxng-mihomo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    restart: unless-stopped
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    command: &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;-d&amp;#34;&lt;/span&gt;, &lt;span class=&#34;s2&#34;&gt;&amp;#34;/root/.config/mihomo&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;    ports:
&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;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;s2&#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;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - ./mihomo:/root/.config/mihomo
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - searxng-net
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    logging:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      driver: json-file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      options:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        max-size: &lt;span class=&#34;s2&#34;&gt;&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;        max-file: &lt;span class=&#34;s2&#34;&gt;&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&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  searxng-net:
&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;volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  searxng-cache:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  valkey-data:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;YAML
&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;cd&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;$APP_DIR&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 class=&#34;k&#34;&gt;if&lt;/span&gt; docker compose version &amp;gt;/dev/null 2&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;1&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;nv&#34;&gt;DC&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&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;k&#34;&gt;elif&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;command&lt;/span&gt; -v docker-compose &amp;gt;/dev/null 2&amp;gt;&lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt;1&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;nv&#34;&gt;DC&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&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;k&#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;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;docker compose / docker-compose not detected&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;## Hermes Only Keeps the Search Entry Point&lt;/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;Hermes doesn&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt;t need to know about Mihomo. It only needs to know that there is a SearXNG on the &lt;span class=&#34;nb&#34;&gt;local&lt;/span&gt; machine:
&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;sb&#34;&gt;```&lt;/span&gt;bash
&lt;/span&gt;&lt;/span&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;Write:&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;Then specify the search backend in &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;If you have the official Hermes SearXNG skill installed, you can continue using it:&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;If Hermes is 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;There is also a boundary here: SearXNG is responsible for search, not for web page content extraction. The Hermes documentation also separates the search backend and extract backend. If you want Hermes to read the content of search result pages later, you still need to configure &lt;code&gt;web.extract_backend&lt;/code&gt; with Firecrawl, Tavily, Exa, Parallel, or other extraction solutions.&lt;/p&gt;
&lt;h2 id=&#34;validation-do-not-skip-layers&#34;&gt;Validation: Do Not Skip Layers
&lt;/h2&gt;&lt;p&gt;Test Mihomo first, don&amp;rsquo;t ask Hermes right away:&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;A returned HTTP status indicates that the host&amp;rsquo;s debug proxy port is available. Then test the 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;If a 403 appears here, first check whether &lt;code&gt;search.formats&lt;/code&gt; includes &lt;code&gt;json&lt;/code&gt;. If it times out or returns empty results here, then check the SearXNG logs:&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;Then send a request in Hermes that will trigger the search:&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;Please search online for the latest information on OpenAI GPT and list the source links.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If &lt;code&gt;/search?...format=json&lt;/code&gt; appears in the logs, it means Hermes is already calling the local SearXNG. Next, let&amp;rsquo;s look at 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;Focus on the subscription updates, provider, health check, and AUTO information. If Mihomo fails to even parse the subscription, then it doesn&amp;rsquo;t matter how correctly SearXNG is configured.&lt;/p&gt;
&lt;h2 id=&#34;boundaries-you-should-not-omit&#34;&gt;Boundaries You Should Not Omit
&lt;/h2&gt;&lt;p&gt;First, do not expose the port to the public network. The &lt;code&gt;compose&lt;/code&gt; in this article only binds to the local machine:&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;Do not change it to:&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;Otherwise, the search service, proxy port, and Mihomo control port are all at risk of being abused on the public network. &lt;code&gt;external-controller: 0.0.0.0:9090&lt;/code&gt; is the listener inside the container, and what truly determines whether it can be accessed externally is the port binding in compose.&lt;/p&gt;
&lt;p&gt;Second, if Google or Startpage consistently time out, do not rush to dismantle the entire chain. Keep Bing, Brave, DuckDuckGo, Wikipedia, and arXiv enabled for now; once the Mihomo subscription and health checks are stable, you can then re-enable the engines that are more prone to triggering CAPTCHAs or timeouts.&lt;/p&gt;
&lt;p&gt;Third, the rollback points for scripts are straightforward:&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.your-timestamp 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.your-timestamp 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;I prefer this layered setup over wrapping the entire server in a global proxy. The problem with a global proxy is that its impact is too broad, and when something goes wrong, it becomes difficult to determine whether the issue lies in the system environment, Docker, application configuration, or the proxy node itself. Hermes, SearXNG, and Mihomo each only handle one thing, which actually makes troubleshooting easier.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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 default 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;This piece retains the parts with the most operational value from the original material: the layered architecture, SearXNG JSON, explicit outgoing proxy, Mihomo provider and health checks, Docker Compose, Hermes configuration, verification commands, FAQ, and rollback. What was trimmed away are repeated explanations and absolutist statements that could mislead; for example, &lt;code&gt;all://:&lt;/code&gt; is not a universally invalid configuration—it simply did not match as expected in that particular environment.&lt;/p&gt;
&lt;p&gt;Additionally, one configuration name has been corrected: the Bing News engine for SearXNG should be written as &lt;code&gt;name: bing news&lt;/code&gt;. If written as &lt;code&gt;name: bing_news&lt;/code&gt;, it won&amp;rsquo;t match the default engine name, and the risk is higher than that of an ordinary spelling issue.&lt;/p&gt;
&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;The user provided a complete deployment material titled &amp;ldquo;Deploying Hermes + SearXNG + Mihomo on Servers in China: Routing Hermes Search Through Proxy and Automatically Selecting Available Nodes&amp;rdquo;, requesting to organize and analyze it, confirm the article content has no errors, and then invoke the blog writing skill to compose it into an article.&lt;/p&gt;
&lt;p&gt;The material includes: background, target architecture, why not use system proxy, SearXNG settings.yml, Mihomo config.yaml, Docker Compose, transformation script, Hermes configuration, verification chain, frequently asked questions, rollback method, and final effect.&lt;/p&gt;
&lt;/blockquote&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Loop engineering moves the person to the checkpoint</title>
        <link>https://ttf248.life/en/p/loop-engineering-human-checkpoints/</link>
        <pubDate>Sun, 14 Jun 2026 07:35:00 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/loop-engineering-human-checkpoints/</guid>
        <description>&lt;p&gt;I attended MiniMax&amp;rsquo;s offline developer conference yesterday, and there&amp;rsquo;s one term that&amp;rsquo;s been stuck in my head ever since I got back: loop engineering.&lt;/p&gt;
&lt;p&gt;At first, I thought it was just a matter of running the agent for a few more rounds. That understanding was too shallow. What really got me stuck is that AI writes code much faster than humans can review it. If human involvement remains stuck at &amp;ldquo;checking every step and making decisions at every step,&amp;rdquo; productivity will eventually be capped by the speed of human review.&lt;/p&gt;
&lt;p&gt;This connects to my recent experience with Codex. When I was writing &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/codex-goal-command-explained/&#34; &gt;that goal article&lt;/a&gt;, I was more focused on the matter of &amp;ldquo;completion criteria&amp;rdquo;: what the goal is, where the boundaries lie, what to use for verification, and when something counts as done.&lt;/p&gt;
&lt;p&gt;Looking back after the conference, &amp;ldquo;goal&amp;rdquo; is only a small but crucial part of loop engineering. It addresses how a single task can keep moving forward without constant human reminders. The bigger questions are: which tasks are worth handing over to a loop, which checkpoints must remain with humans, which judgments can be built into the system in advance, and which responsibilities cannot be pushed onto AI.&lt;/p&gt;
&lt;p&gt;I now think there are two scenarios that fit best.&lt;/p&gt;
&lt;p&gt;One is performance optimization. It naturally comes with metrics, and it&amp;rsquo;s best if it can also automatically retest. You tell the agent: the interface P95 needs to be reduced to a certain value, the first-screen rendering needs to be controlled within a certain number of milliseconds, the bundle size needs to be reduced to a certain number of KB, and regression tests must not fail. After each round of changes, it runs a benchmark, profile, or stress test; if the target isn&amp;rsquo;t met, it continues to look for bottlenecks. Here, the human&amp;rsquo;s value is not to check which line was changed each time, but to first provide verifiable acceptance metrics, and then judge at key checkpoints whether &amp;ldquo;this optimization has changed the business semantics.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Another one is UI refactoring. The premise is having clear design specs, ideally achieving a 1:1 restoration. Without design specs, people keep getting pulled back into trivial judgments like &amp;ldquo;this margin is off,&amp;rdquo; &amp;ldquo;this color feels wrong,&amp;rdquo; &amp;ldquo;this interaction doesn&amp;rsquo;t feel right.&amp;rdquo; With design specs, AI&amp;rsquo;s loop can converge around screenshots, DOM, styles, and visual differences. People don&amp;rsquo;t need to chase every CSS adjustment, but they are responsible for the final interface.&lt;/p&gt;
&lt;p&gt;The common ground between these two scenarios is that the deliverable is not just a vague &amp;ldquo;do it better.&amp;rdquo; Performance optimization has numbers, UI redesigns have design mockups. The loop works not because the AI is more obedient, but because the task itself has comparable goals.&lt;/p&gt;
&lt;h2 id=&#34;multica-is-like-a-collaboration-board&#34;&gt;Multica Is Like a Collaboration Board
&lt;/h2&gt;&lt;p&gt;At the conference venue, I also ran into people from the open-source community introducing their projects, among which Multica was quite interesting. When I looked it up afterward, I found that it isn&amp;rsquo;t a brand-new monolithic coding agent, but rather a layer that connects tools such as Claude Code, Codex, GitHub Copilot CLI, OpenCode, Gemini, Kimi, and Cursor Agent into a unified task collaboration layer.&lt;/p&gt;
&lt;p&gt;In its README, agent is described as a teammate: it can be assigned issues, report blockers, update status, and also appear on the kanban board, in comments, and across the task lifecycle. More specifically in the docs: agent is a first-class member of the workspace, can be assigned issues, speak in comments, and be mentioned with &lt;code&gt;@&lt;/code&gt;; when creating an agent, you can choose the underlying AI coding tool behind it, and configure instructions, model, environment variables, CLI arguments, visibility, and concurrency limits.&lt;/p&gt;
&lt;p&gt;This turns &amp;ldquo;which model to use&amp;rdquo; from a temporary choice in a single prompt into a role configuration in team collaboration.&lt;/p&gt;
&lt;p&gt;The most interesting aspect of it is not the phrase &amp;ldquo;multi-agent&amp;rdquo; itself, but rather the ability to assign different roles to different agents. For example, one agent can use a cheap model to handle repetitive fixes, another can use a more powerful model to make architectural judgments, and a third can focus solely on code review without making any changes; combined with mechanisms like Squad, a leader agent can route tasks to the appropriate members based on the content of the issue.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t run through Multica end to end, so it can only be treated here as a sample that can be cross-checked against publicly available material. But this design direction belongs to the same class of problem as loop engineering: rather than letting a single AI do everything, you put tasks, roles, models, review, and state transitions all into the same loop.&lt;/p&gt;
&lt;p&gt;If a company is really going to implement AI to write code, this difference matters.&lt;/p&gt;
&lt;p&gt;Now model prices vary, and so do their capability boundaries. Using the most expensive closed-source model for every task doesn&amp;rsquo;t necessarily keep costs under control; using cheap models for every task may push costs to the back end through rework and missed reviews. A more realistic approach is layering: low-risk, repetitive, clearly bounded work goes to cheap models first; critical changes, architectural trade-offs, and final review are reserved for stronger models or humans.&lt;/p&gt;
&lt;p&gt;Using closed-source strong models for review is, I think, a good position. Review is not just &amp;ldquo;picking out grammatical errors&amp;rdquo; — it requires examining whether requirements have been corrupted, whether boundaries have been crossed, whether tests only cover the surface, and whether commit messages conceal risks. This type of task demands more judgment, but the call frequency may not be as high as during the implementation phase, making the cost accounting easier to accept.&lt;/p&gt;
&lt;h2 id=&#34;less-human-involvement-does-not-mean-less-human-accountability&#34;&gt;Less Human Involvement Does Not Mean Less Human Accountability
&lt;/h2&gt;&lt;p&gt;There&amp;rsquo;s a point from the roundtable discussion that I really agree with: the code is written by AI, but the code is submitted by humans.&lt;/p&gt;
&lt;p&gt;This sentence sounds like a reminder of responsibility, but it is also a principle of process design. AI can write code, modify code, run tests, generate PR descriptions, and even have another model review it first. But the person who ultimately merges the code into the main branch cannot say, &amp;ldquo;This was written by AI, it has nothing to do with me.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;What a company needs is not who is typing characters on a keyboard, but who takes responsibility for the result. Once you submit, it means you are willing to take responsibility for the business semantics, risk boundaries, test evidence, and rollback plan of this change. The deeper AI&amp;rsquo;s involvement, the less ambiguous this matter can be.&lt;/p&gt;
&lt;p&gt;So loop engineering is not about completely removing people from the process. It&amp;rsquo;s more about rearranging where people fit in:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; relref &amp;ldquo;/post/2025-03-15-llm-app-engineering/&amp;rdquo; &amp;gt;}}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Before the task begins, humans clearly define the goals, boundaries, and acceptance criteria.&lt;/li&gt;
&lt;li&gt;During the loop, the system and the agent handle repeated attempts, testing, fixing, and state synchronization on their own.&lt;/li&gt;
&lt;li&gt;At critical checkpoints, humans review the evidence, the differences, and the risks, rather than chasing every line of the AI&amp;rsquo;s output.&lt;/li&gt;
&lt;li&gt;When submitting code, humans take responsibility for the outcome and cannot use AI as an excuse to avoid accountability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is also why I connect seemingly scattered elements such as goals, performance metrics, design specs, and Multica together. They all advance, externalize, and structure human judgment. Humans participate a little less, but in more critical positions.&lt;/p&gt;
&lt;p&gt;If this isn&amp;rsquo;t done well, it turns into another kind of inefficiency: AI produces a lot, humans can&amp;rsquo;t review it all, and the team ends up merging code based on &amp;ldquo;it feels about right.&amp;rdquo; When something goes wrong, the blame gets pushed onto AI. That&amp;rsquo;s not engineering—that&amp;rsquo;s just chaos with a different producer.&lt;/p&gt;
&lt;p&gt;The loop that&amp;rsquo;s really worth pursuing is not about letting AI keep writing, but about making sure that every round of writing, modification, testing, and review can come back to the same acceptance checklist. Human work shifts from &amp;ldquo;watching it write&amp;rdquo; to &amp;ldquo;defining what counts as good, checking whether the evidence is sufficient, and taking responsibility for submissions.&amp;rdquo; That might be exactly where the term loop engineering has kept me thinking.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer attended the Minimax offline developer conference yesterday. There&amp;rsquo;s something that has been on my mind—loop engineering. Recently I&amp;rsquo;ve been developing with codex and also enjoy using goals. I&amp;rsquo;ve written articles about goals before and discovered two scenarios where they fit best: performance optimization, where you directly provide the required performance metrics, and UI refactoring, where you provide design drafts and require 1:1 restoration. Going back to loop engineering mentioned at the beginning—programming in the AI era. Since AI productivity far exceeds that of humans, the speed of human review simply cannot keep up with the speed of AI output. However, with current vibe coding, many decisions still require human involvement. Once human participation increases, the entire process becomes difficult to fully automate. The concept of loop engineering is more about reducing the degree of human involvement.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I also met people from the open-source community who came to present their projects. One project was quite interesting—Multica. Check out the relevant materials and briefly introduce it. Apart from its design philosophy, the most interesting aspect is that different models are given different identities, which can effectively leverage various large models. After all, model prices vary nowadays, and using closed-source models for review is a good idea.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Regarding the implementation of AI code in companies, during the roundtable discussion, there was a consensus on one point: the code is written by AI, but the code is submitted by humans. You need to be responsible for the code you submit, which reflects your sense of responsibility and your attitude toward doing things. You can&amp;rsquo;t just say it was written by AI and has nothing to do with you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-approach-summary&#34;&gt;Writing Approach Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Kept the in-person trigger of attending the MiniMax offline developer conference on 2026-06-13, without expanding it into a conference report.&lt;/li&gt;
&lt;li&gt;Refocused the main thread on &amp;ldquo;how the role of human participation changes,&amp;rdquo; rather than explaining loop engineering, goals, Multica, and corporate responsibility item by item.&lt;/li&gt;
&lt;li&gt;The Multica section only describes capabilities supported by public materials, and explicitly places &amp;ldquo;using closed-source models for review&amp;rdquo; within the author&amp;rsquo;s judgment.&lt;/li&gt;
&lt;li&gt;Suppressed the elaboration of specific model pricing, company management systems, and code review processes, to prevent the article from drifting from workflow observations into management policy recommendations.&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>After Codex Fails at Writing UI, Let ChatGPT Draw the UI</title>
        <link>https://ttf248.life/en/p/codex-chatgpt-ui-mockup-workflow/</link>
        <pubDate>Sun, 14 Jun 2026 05:14:22 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/codex-chatgpt-ui-mockup-workflow/</guid>
        <description>&lt;p&gt;At first I thought it was just that the frontend hadn&amp;rsquo;t had time to tidy things up. The business logic in &lt;code&gt;strategy_studio&lt;/code&gt; has been moving forward: market data sync, PostgreSQL, asynchronous backtesting, report persistence, and the research workbench have all been wired in one after another, but the pages look more and more like &amp;ldquo;features got on board first, the UI buys a ticket later.&amp;rdquo; When I wanted Codex to do a destructive refactor of the UI directly, the real problem only then surfaced: no matter how harsh I made the prompts, the interface still looked like components being shifted around inside the old grid.&lt;/p&gt;
&lt;p&gt;This wasn&amp;rsquo;t the first time I used Codex to modify code. For tasks like backend pipelines, API integration, test fixes, and report generation, I usually just throw the objective in, let it read files, modify code, run validation, and commit. The problem arose with this frontend refactor: I hadn&amp;rsquo;t done a UI draft first, and the business structure had already been laid out. Once the old interface exists, what the model sees is not just the requirements, but also ready-made components, ready-made routes, ready-made styles, and a bunch of historical inertia.&lt;/p&gt;
&lt;p&gt;My initial reaction was also a typical one: just keep adding prompts. Allow destructive refactoring, redesign the UI and interaction logic, don&amp;rsquo;t make the elements too dense, adapt it for 24-inch and 32-inch monitors, focus on core features, and don&amp;rsquo;t be conservative. Every sentence looked correct on its own, but taken together they still lacked one thing: what it should ultimately look like.&lt;/p&gt;
&lt;h2 id=&#34;whats-stuck-isnt-the-button-color&#34;&gt;What&amp;rsquo;s Stuck Isn&amp;rsquo;t the Button Color
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;strategy_studio&lt;/code&gt; is not a small tool with only two or three buttons. The README already positions it as a Chinese-first strategy research platform: a Next.js frontend, FastAPI API, PostgreSQL, Worker, Scheduler, and the Yahoo synchronization pipeline all running together. The frontend README further converges the pages into a single research workbench: sample preparation, experiment configuration, task tracking, result review, template maintenance, as well as report details and operations/maintenance entry points.&lt;/p&gt;
&lt;p&gt;The front-end issues of this kind of project, on the surface, are &amp;ldquo;not good-looking enough,&amp;rdquo; but in reality, the information hierarchy hasn&amp;rsquo;t been established. Should users look at market data first, or look at backtesting tasks first? Among the curve, indicators, input parameters, and re-run button in the report details, which is more important? Is the homepage a navigation page or a workbench? Should the large screen be filled up, or should it leave some breathing room?&lt;/p&gt;
&lt;p&gt;These instructions are described only in words, and Codex easily interprets them as &amp;ldquo;make some tweaks to the existing page.&amp;rdquo; It will modify components, adjust spacing, change colors, but still follow the old structure. Allowing destructive refactoring isn&amp;rsquo;t enough either, because destructive refactoring addresses whether the code can be heavily modified, not whether there is an acceptable visual goal.&lt;/p&gt;
&lt;p&gt;I went out during the day, and suddenly thought of something in the evening: don&amp;rsquo;t let Codex keep guessing the UI. First, draw the UI design mockup on the ChatGPT web client, then hand the image over to Codex to recreate it.&lt;/p&gt;
&lt;p&gt;The prompt used at the time was very straightforward:&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 Analyze the front-end code of the current project, understand the business logic, and as a professional UI designer and interaction designer, help me redesign a set of front-end pages. Produce a separate design for each page, and make sure they are adapted for 24-inch and 32-inch monitors. The elements should not be too dense, so that users can easily focus on the core functions.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result produced by this step is much more stable than continuing to pile up adjectives in Codex. It first provides a visible target: how the page is laid out, how the module proportions are divided, where the visual center is, and roughly how much whitespace is used. When Codex takes over afterward, the task shifts from &amp;ldquo;redesigning based on aesthetics&amp;rdquo; to &amp;ldquo;understanding the business from the diagram and recreating the code.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;the-color-scheme-should-also-be-turned-into-material-first&#34;&gt;The color scheme should also be turned into material first
&lt;/h2&gt;&lt;p&gt;The first round of diagrams solved the layout issue, but the default color scheme is still the standard blue-and-white of a regular admin system, which doesn&amp;rsquo;t quite match my aesthetic. If at this point I just throw &amp;ldquo;I don&amp;rsquo;t like blue-and-white, switch to a more sophisticated color scheme&amp;rdquo; to Codex, it&amp;rsquo;s essentially still letting it guess.&lt;/p&gt;
&lt;p&gt;This is the initial blue-and-white color scheme. It already illustrates the page structure, but the overall feel is still rather generic and resembles an ordinary admin dashboard.&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 initial draft blue and white UI design&#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;I changed my approach afterward: first, I discussed the color scheme direction on the ChatGPT web client, making the base color, accent color, card hierarchy, and the feel of a research tool clear, then asked the image model to show its intent based on this direction. It is not an official Figma design, nor is it a complete design system, but it is already sufficient to lock down the things most likely to drift during the front-end refactor.&lt;/p&gt;
&lt;p&gt;What we ultimately got was not a single &amp;ldquo;big-and-comprehensive homepage&amp;rdquo; design, but design drafts broken down by page: Homepage, Market Research, Backtest Experiment, Report Center, Report Details, Strategy Templates, Operations &amp;amp; Maintenance. This breakdown is critical. The frontend of &lt;code&gt;strategy_studio&lt;/code&gt; is not a marketing page but a research tool; the Homepage, Market page, and Report Details page each serve different reading tasks and cannot all be crammed into the same set of card grids.&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 Homepage Design Draft&#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 Report Details Design Draft&#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;Once the design comes in, Codex&amp;rsquo;s role becomes clear. It doesn&amp;rsquo;t act as a UI designer from scratch; instead, it reads the existing business logic, breaks down components, adjusts layouts, aligns interaction states, runs lint/build, and then refactors the legacy frontend to a target state that closely matches the design mockups.&lt;/p&gt;
&lt;h2 id=&#34;not-just-a-smarter-web-version&#34;&gt;Not just a &amp;ldquo;smarter web version&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;Later, I checked the official materials again, trying to confirm whether this issue could be explained as &amp;ldquo;ChatGPT has different intelligence capabilities across different channels,&amp;rdquo; or as &amp;ldquo;Codex has its features gutted when used directly.&amp;rdquo; That explanation is too crude.&lt;/p&gt;
&lt;p&gt;OpenAI&amp;rsquo;s description of Codex CLI is a coding agent in the local terminal: it can read code, modify code, and run commands in the current directory. Codex web, on the other hand, handles code tasks in a cloud environment. When OpenAI discusses the Codex agent loop, the focus is not on &amp;ldquo;the model coming up with everything out of thin air,&amp;rdquo; but rather on how model reasoning, context management, tool invocation, file read/write, and command execution together constitute a software task.&lt;/p&gt;
&lt;p&gt;ChatGPT Images is another entry point. According to OpenAI Help&amp;rsquo;s description of Images in ChatGPT, users can create and edit images in conversations, and also create mockups or creative visuals. This product form is naturally more suitable for exploring visual goals first, because its deliverable is the image itself, rather than immediately diving into code.&lt;/p&gt;
&lt;p&gt;So the difference this time isn&amp;rsquo;t &amp;ldquo;who is smarter,&amp;rdquo; but rather that the task forms are different. Hand &amp;ldquo;redesigning the UI&amp;rdquo; directly to Codex, and it will think from within the code repository: which files need to be changed, which components cannot be broken, and how to make the build pass. Hand the same business context first to ChatGPT Images, and it will first compress the abstract aesthetics and information hierarchy into a visible target image.&lt;/p&gt;
&lt;p&gt;This also explains why Codex later became easier to use. It&amp;rsquo;s not that the design mockups bypassed Codex, but rather that the design mockups supplemented Codex with context and acceptance criteria. The official Codex prompting documentation also emphasizes that you need to provide Codex with relevant files, images, and clear completion standards. Without images, &amp;ldquo;the elements shouldn&amp;rsquo;t be too dense&amp;rdquo; is just an adjective; with images, it becomes a reproducible layout constraint.&lt;/p&gt;
&lt;h2 id=&#34;smoother-chain&#34;&gt;Smoother Chain
&lt;/h2&gt;&lt;p&gt;After this, I&amp;rsquo;ll break down this kind of frontend refactoring into three parts.&lt;/p&gt;
&lt;p&gt;In the first stage, start with visual exploration. Have the ChatGPT web client read the repository, understand the business, and generate diagrams per page, then iterate on color schemes, density, large-screen adaptation, and visual hierarchy. The output of this stage is not code, but target diagrams.&lt;/p&gt;
&lt;p&gt;In the second step, hand over the target screenshots and engineering constraints to Codex. The prompt should not stop at &amp;ldquo;redesign the UI&amp;rdquo;; it needs to clearly specify which images are the targets, which business logic must not be broken, which pages are allowed to undergo breaking refactors, and which tests and builds must pass.&lt;/p&gt;
&lt;p&gt;The third phase enters engineering restoration. This is where it becomes appropriate to have Codex run in goal or continuous task mode: first read the frontend structure, then split the implementation by page, run verification after each page is completed, and finally perform a unified check of interactions and responsiveness.&lt;/p&gt;
&lt;p&gt;I used to treat &amp;ldquo;writing harsher prompts&amp;rdquo; as the solution. Looking back now, what&amp;rsquo;s more useful in frontend refactoring isn&amp;rsquo;t harshness, but filling in the missing intermediate artifacts. Without UI mockups, disruptive refactoring just breaks things apart further; with UI mockups, disruptive refactoring finally has direction.&lt;/p&gt;
&lt;p&gt;This workflow is not only suitable for &lt;code&gt;strategy_studio&lt;/code&gt;. Any personal project that already has business logic but whose frontend has long been patched together as it goes along can be handled the same way: first let the image model draw out the interface targets, then let Codex turn those targets into code. The models are not replacing one another; each one takes on the segment that better matches its own kind of work.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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 Repository&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 Design Drafts Directory&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;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;In daily use of codex for calling the ChatGPT model to write code during the development of the open-source project &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;, I started off without creating UI mockups and went straight into business logic development, which resulted in an unplanned front-end interface. I tried writing prompts directly in codex — allowing destructive refactoring, redesigning the UI and interaction logic — but no matter how I adjusted them, the results were never quite right. At that point, I still hadn&amp;rsquo;t suspected anything wrong with codex itself. During the day, I went out, and at night, inspiration struck: what if I used the web version to call image2 to generate a set of UI design mockups, then handed them off to codex running the goal model to faithfully reproduce them in code? I gave it a quick try and it worked without any issues. Here is the prompt I used:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&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;https://github.com/ttf248/strategy_studio&lt;/a&gt; Analyze the current project&amp;rsquo;s front-end code, understand the business logic, and act as a professional UI designer and interaction designer to help me redesign a complete set of front-end pages. Generate a separate image for each page, making sure they adapt to 24-inch and 32-inch monitors. Keep elements from being too densely packed so users can focus on the core functionality.&lt;/p&gt;
&lt;p&gt;At this point the results were already pretty good, but the default color scheme — the conventional blue-and-white — didn&amp;rsquo;t match my aesthetic taste. So I continued refining the approach: first, we discussed and agreed on a suitable color palette through web chat; then, image generated a demo based on that palette; finally, combining the color scheme with the previous prompt, we got this:&lt;/p&gt;
&lt;p&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;https://github.com/ttf248/strategy_studio/tree/main/ui&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve written a lot of content — please organize it and wrap it up into a single blog post. For any questions you&amp;rsquo;re unsure about, search the web on your own for reference. The repository at &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; contains a lot of images; feel free to pick a couple and embed them in the blog post.&lt;/p&gt;
&lt;h3 id=&#34;writing-approach-summary&#34;&gt;Writing Approach Summary
&lt;/h3&gt;&lt;p&gt;This rewrite preserves the original&amp;rsquo;s judgment and materials, but changes the opening from &amp;ldquo;here is the complete answer first&amp;rdquo; to &amp;ldquo;let&amp;rsquo;s first step into the scene where the frontend refactor stalled.&amp;rdquo; The article trims away generalized model reviews and focuses only on the shift that actually worked in this workflow: turning the visual goal into a diagram first, then letting Codex handle the engineering restoration.&lt;/p&gt;

    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        <item>
        <title>Codex goal embeds the completion criteria within the task itself</title>
        <link>https://ttf248.life/en/p/codex-goal-command-explained/</link>
        <pubDate>Wed, 27 May 2026 20:11:41 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/codex-goal-command-explained/</guid>
        <description>&lt;p&gt;&lt;code&gt;/goal&lt;/code&gt; is easily misinterpreted as a command to &amp;ldquo;let the agent work for a bit longer.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This, of course, is merely its surface manifestation. If you give Codex a goal, it can continuously progress around that objective, instead of stopping after a single round of answers. But what is truly noteworthy is not how long it &amp;ldquo;runs,&amp;rdquo; but rather that it converts &amp;ldquo;what constitutes completion&amp;rdquo; from a temporary reminder into an intrinsic part of the task itself.&lt;/p&gt;
&lt;p&gt;A standard prompt describes what needs to happen next. A &lt;code&gt;goal&lt;/code&gt;, however, is more like attaching a checklist/acceptance form to an agent: What is the objective? Where are the boundaries? Which validations must pass? What conditions must be met for it to be considered complete?&lt;/p&gt;
&lt;h2 id=&#34;goal-is-not-the-continue-button&#34;&gt;goal is not the continue button
&lt;/h2&gt;&lt;p&gt;If we only look at its command form, &lt;code&gt;/goal&lt;/code&gt; resembles an enhanced version of &amp;ldquo;continue until complete.&amp;rdquo; However, this shifts the focus.&lt;/p&gt;
&lt;p&gt;The hardest part of long tasks isn&amp;rsquo;t whether the model wants to continue, but who decides whether or not to proceed after each round.&lt;/p&gt;
&lt;p&gt;When you ask an agent to migrate a frontend project, it might stop simply after adjusting the routes, believing the job is done; when you ask it to fix a test, it might just make the current failing use case pass and quit; and when you ask it to rewrite a batch of articles, it might think it&amp;rsquo;s handled all the critical parts by only tweaking a few high-risk drafts.&lt;/p&gt;
&lt;p&gt;These stopping points may not be incorrect, but they often differ from human acceptance criteria (or: human standards/judgments).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;goal&lt;/code&gt; is to solve this: clearly define the acceptance criteria beforehand, so that every subsequent round can judge against it.&lt;/p&gt;
&lt;p&gt;A broad objective is as follows:&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 Help me migrate the frontend to Next.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Its problem isn&amp;rsquo;t that its output is too short, but rather that it lacks a definite termination/stopping condition. Codex can rewrite multiple pages, can restructure components on the fly, and also continuously fill in whatever content it deems necessary.&lt;/p&gt;
&lt;p&gt;A more usable way would be something like this:&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 Migrate the order management backend from React Router to Next.js App Router.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The visual behavior of the login page, order list, order details, and checkout page must be consistent with the old version.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Do not change the API contracts or database schemas.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;After completing a batch of pages, run npm run build, npm test, and Playwright critical paths.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Only when all these validations pass can it be considered complete.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The extra details in this paragraph are not filler; they constitute four control planes:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Element&lt;/th&gt;
					&lt;th&gt;Role&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Goal&lt;/td&gt;
					&lt;td&gt;What is the final desired outcome?&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Boundary&lt;/td&gt;
					&lt;td&gt;Which interfaces, data, files, or behaviors cannot be handled manually?&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Validation&lt;/td&gt;
					&lt;td&gt;What evidence proves that it was truly completed?&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Termination&lt;/td&gt;
					&lt;td&gt;After fulfilling what conditions can it stop?&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These four things are what raise the &lt;code&gt;goal&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;why-does-it-run-for-a-long-time&#34;&gt;Why Does It Run For A Long Time?
&lt;/h2&gt;&lt;p&gt;Codex can continue to make progress on the goal, not because a single answer was elongated.&lt;/p&gt;
&lt;p&gt;The actual workflow resembles a loop: plan, execute, observe tool results, revise, and then decide whether or not to continue. Build failures, test failures, screenshot inconsistencies, lint errors, or failed evaluation samples will all send the task back to the next iteration.&lt;/p&gt;
&lt;p&gt;If the goal specifies a verification method, the agent cannot simply declare it &amp;ldquo;should be fine&amp;rdquo; based on intuition. It must obtain evidence. If the evidence hasn&amp;rsquo;t returned, further investigation continues; if the evidence fails, remediation is required; only when all evidence passes can the work be signed off.&lt;/p&gt;
&lt;p&gt;This is why &lt;code&gt;goal&lt;/code&gt; is suitable for tasks such as transfer, refactoring, batch revision, prompt eval, and troubleshooting long pipelines. Their common characteristic is that they cannot be completed in a single pass, and their completion cannot rely solely on subjective judgment.&lt;/p&gt;
&lt;p&gt;Conversely, such a goal is very dangerous:&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 Think of a more advanced product solution
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It lacks boundaries, validation, or a stopping condition. The agent might run for a long time, but running for a long time does not equate to usefulness. At a minimum, you must clearly state how many sets of solutions are produced, what constraints are covered, what criteria are used for filtering, and when it should stop.&lt;/p&gt;
&lt;h2 id=&#34;claude-code-is-also-handling-the-same-thing&#34;&gt;Claude Code is also handling the same thing
&lt;/h2&gt;&lt;p&gt;Claude Code also has &lt;code&gt;/goal&lt;/code&gt;. The official documentation explains it more directly: The user sets a completion condition, and Claude will continue working across turns until the condition is met.&lt;/p&gt;
&lt;p&gt;The Claude Code documentation also mentions that at the end of each round, it checks whether the completion condition is met; if not, it proceeds to the next round. This point is critical because it externalizes the action of &amp;ldquo;proceeding&amp;rdquo; from the model&amp;rsquo;s own subjective conclusion and turns it into an additional conditional judgment.&lt;/p&gt;
&lt;p&gt;The specific implementation details of the two companies do not need to be forced to be equivalent, but their direction is consistent: the terminal agent is moving from &amp;ldquo;executing the next instruction&amp;rdquo; towards &amp;ldquo;continuously progressing around verifiable goals.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;We can simply categorize it as:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Capability&lt;/th&gt;
					&lt;th&gt;What problem it addresses&lt;/th&gt;
					&lt;th&gt;Suitable Scenarios&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;Defines explicit completion criteria and advances to verifiable results across multiple rounds&lt;/td&gt;
					&lt;td&gt;Migration, Refactoring, Batch Fixing, Long-running tasks&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;/loop&lt;/code&gt; or looping capabilities&lt;/td&gt;
					&lt;td&gt;Allows the same task to execute repeatedly based on count or condition&lt;/td&gt;
					&lt;td&gt;Retries, Generating candidates, Batch exploration&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;hooks&lt;/td&gt;
					&lt;td&gt;Automatically executes rules on fixed events&lt;/td&gt;
					&lt;td&gt;Formatting, Testing, Notification, Logging&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Sub-agent/Multi-agent view&lt;/td&gt;
					&lt;td&gt;Decomposes tasks for observation and progression by different worker threads&lt;/td&gt;
					&lt;td&gt;Parallel analysis, Modular implementation, Long-term background tasks&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Memory/Project README file&lt;/td&gt;
					&lt;td&gt;Solidifies long-term constraints and repository rules&lt;/td&gt;
					&lt;td&gt;Team standards, Code style, Tool entry points&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In this table, the position of &lt;code&gt;goal&lt;/code&gt; is very clear: it neither replaces hooks nor replaces memory. It manages &amp;ldquo;what level constitutes completion for this task.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;goals-should-be-written-like-acceptance-criteria&#34;&gt;Goals should be written like acceptance criteria
&lt;/h2&gt;&lt;p&gt;I will now write a goal out in four lines:&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;Objective: What final user-visible result must appear.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Boundaries: Which files, interfaces, data, visual elements, or behaviors cannot be modified/tampered with.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Verification: What commands, tests, screenshots, evaluations, or manual checks serve as evidence.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Stopping Condition: Stop when all conditions are met; pause when encountering specific permissions, facts, or product judgments.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is very different from a regular prompt.&lt;/p&gt;
&lt;p&gt;A regular prompt is more like a next step action, while a goal is more like the completion criteria. It&amp;rsquo;s not about making the human disappear from the process; rather, it&amp;rsquo;s about embedding human judgment upfront. You no longer have to remind it round after round that &amp;ldquo;this isn&amp;rsquo;t complete,&amp;rdquo; but instead write down what constitutes completion as mandatory conditions from the very beginning.&lt;/p&gt;
&lt;p&gt;Therefore, the more you want the agent to run autonomously, the narrower you must define its goals.&lt;/p&gt;
&lt;p&gt;The less you focus on monitoring the process, the more rigorous the validation documentation must be.&lt;/p&gt;
&lt;p&gt;The more you don&amp;rsquo;t want it to deviate, the clearer you must write the boundaries.&lt;/p&gt;
&lt;p&gt;This is where &lt;code&gt;goal&lt;/code&gt; truly deserves attention. It&amp;rsquo;s not about adding more commands, nor is it how long it can run; rather, it&amp;rsquo;s that the terminal agent is beginning to bring &amp;ldquo;who determines completion&amp;rdquo; into focus.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;h2 id=&#34;authors-notes&#34;&gt;Author&amp;rsquo;s Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$blog-writer Detail Codex&amp;#39;s newly released goal command: what is its working principle, why does it run for such a long time, and what are the official examples? Does Claude Code have similar naming/commands? Additionally, compile the useful and popular new features recently released by two terminal tools into a table.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;writing-idea-outline&#34;&gt;Writing Idea Outline
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Keep the original main judgment: The core of &lt;code&gt;goal&lt;/code&gt; is not the command name, but the completion condition.&lt;/li&gt;
&lt;li&gt;Reinsert the Claude Code comparison and terminal function table required in the original prompt.&lt;/li&gt;
&lt;li&gt;Eliminate &amp;ldquo;official document recitation&amp;rdquo; and focus on how to write a usable goal.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>How do NVIDIA data center GPUs iterate after the release of ChatGPT?</title>
        <link>https://ttf248.life/en/p/nvidia-data-center-gpu-since-chatgpt/</link>
        <pubDate>Fri, 15 May 2026 19:58:51 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/nvidia-data-center-gpu-since-chatgpt/</guid>
        <description>&lt;p&gt;Let&amp;rsquo;s first establish the date. ChatGPT&amp;rsquo;s public research preview version was released on November 30, 2022, not 2023. [1]&lt;/p&gt;
&lt;p&gt;After this point, NVIDIA&amp;rsquo;s data center GPU main roadmap is quite clear: The conclusion of Ampere, followed by Hopper taking over. Hopper focused on expanding VRAM capacity and refresh rate, while Blackwell will shift its focus from &amp;ldquo;single-card dense compute power&amp;rdquo; toward &amp;ldquo;inference throughput, power consumption, and system-level interconnection.&amp;rdquo; The China-specific versions represent a different story: A800, H800, and H20 are fundamentally compliance versions created under US export control constraints, and therefore cannot be viewed using the same metrics as the global flagship line.&lt;/p&gt;
&lt;p&gt;This only counts two lines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Global Data Center Training/Inference Mainline: A100 as the control baseline, H100, H200, B200, B300.&lt;/li&gt;
&lt;li&gt;China Dedicated Line: A800, H800, H20.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I didn&amp;rsquo;t include the L4, L40, L40S, and L2 in the main text either. It&amp;rsquo;s not that they aren&amp;rsquo;t important; it&amp;rsquo;s just that they are more related to the video inference, general inference, graphics, and virtualization line, and mixing them with the large model training main line of A100/H100/H200/B200 would confuse the pricing and performance metrics.&lt;/p&gt;
&lt;h2 id=&#34;look-at-the-main-storyline&#34;&gt;Look at the Main Storyline
&lt;/h2&gt;&lt;p&gt;To conclude: If we only look at the release cadence after November 30, 2022, H100 was the true starting point for the early generative AI explosion; H200 was a refresh card designed to &amp;ldquo;fill the memory gap&amp;rdquo;; B200 represents what truly constitutes a platform-level generational replacement, and B300 pushes Blackwell one step further into the inference and reasoning era.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Model&lt;/th&gt;
					&lt;th&gt;Release Date&lt;/th&gt;
					&lt;th&gt;Architecture&lt;/th&gt;
					&lt;th&gt;VRAM&lt;/th&gt;
					&lt;th&gt;VRAM Bandwidth&lt;/th&gt;
					&lt;th&gt;Interconnect&lt;/th&gt;
					&lt;th&gt;Official Performance Metrics&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;Nov 2020, as comparative baseline&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;BF16/FP16 Tensor Core 312 TFLOPS, INT8 624 TOPS [2]&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;BF16/FP16 1,979 TFLOPS, FP8 3,958 TFLOPS; DGX H100 single node 32 PFLOPS FP8, a 6x increase over DGX A100 [3][4]&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 Refresh&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;The focus provided by Nvidia is not doubled core compute power, but rather 1.9x inference for Llama2 70B and 1.6x for GPT-3 175B; relative to H100, it features larger and faster memory [5][6]&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;B200 SXM&lt;/td&gt;
					&lt;td&gt;202&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&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;What&amp;rsquo;s easy to misunderstand here is that the H200 is not a &amp;ldquo;brute-force double compute card&amp;rdquo;; rather, it is more like supplementary knowledge built upon the Hopper era. Once large model training and inference enter the stage of ultra-long contexts, massive KV caches, MoE (Mixture of Experts), and larger batch sizes, the bottleneck is no longer solely determined by the BF16 peak number, but rather by VRAM capacity and VRAM bandwidth. The H200 addresses this shortcoming.&lt;/p&gt;
&lt;p&gt;The true generational leap happens with Blackwell. Blackwell is no longer just selling a single card; it is selling an entire suite of platform capabilities: new precision, interconnectivity, system-level bandwidth, inference cost, power efficiency, and rack-scale architecture. This is why when many sources discuss the B200, the single-card metrics are not as immediately understandable at a glance as those for the H100. This is because NVIDIA&amp;rsquo;s narrative focus has shifted from &amp;ldquo;how many TFLOPS does this card have&amp;rdquo; to &amp;ldquo;what size model can this entire system run and what will the cost be.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;take-another-look-at-chinese-specialty-lines&#34;&gt;Take Another Look at Chinese Specialty Lines
&lt;/h2&gt;&lt;p&gt;The China exclusive line must be viewed separately. Because its goal is not to defeat global flagship cards, but rather to stay below the export control red line while retaining commercial usability.&lt;/p&gt;
&lt;p&gt;The most memorable sentence from this discussion is: A800 and H800 are more like &amp;ldquo;reducing interconnectivity,&amp;rdquo; while H20 involves &amp;ldquo;continued restrictions even on computing capability.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Therefore, if someone only looks at the memory specification and draws the conclusion that &amp;ldquo;H20 is newer than H800, so it must be stronger,&amp;rdquo; this judgment is flawed. While H20&amp;rsquo;s 96GB HBM3 and 4.0 TB/s bandwidth look impressive, its very existence is predicated on meeting stricter export limitations. Its commercial goal is first to be sellable, and only secondly to maximize usability.&lt;/p&gt;
&lt;h2 id=&#34;what-are-the-upgrades-compared-to-the-previous-generation&#34;&gt;What are the upgrades compared to the previous generation?
&lt;/h2&gt;&lt;p&gt;First, let&amp;rsquo;s talk about computation methods:&lt;/p&gt;
\[
\text{Upgrade Rate}=\frac{\text{New Indicator}-\text{Previous Indicator}}{\text{Previous Indicator}}
\]&lt;p&gt;However, this formula is only suitable for metrics with consistent definitions. VRAM, memory bandwidth, and NVLink bandwidth can be calculated directly; however, platform-level inference cost and whole machine throughput cannot be forced back into the single-card TFLOPS metric framework.&lt;/p&gt;
&lt;h3 id=&#34;global-key-themes&#34;&gt;Global Key Themes
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Generation&lt;/th&gt;
					&lt;th&gt;Biggest Change&lt;/th&gt;
					&lt;th&gt;Quantifiable Upgrade Magnitude&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 and memory bandwidth rise together&lt;/td&gt;
					&lt;td&gt;Memory capacity 0%; memory bandwidth from 2.039 to 3.35 TB/s, approx +64.3%; NVLink from 600 to 900 GB/s, approx +50%; BF16/FP16 from 312 to 1,979 TFLOPS, approx +534.3% [2][3]&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H100 SXM -&amp;gt; H200 SXM&lt;/td&gt;
					&lt;td&gt;Focus shifted to &amp;ldquo;larger and faster memory&amp;rdquo;&lt;/td&gt;
					&lt;td&gt;Memory from 80GB to 141GB, approx +76.3%; memory bandwidth from 3.35 to 4.8 TB/s, approx +43.3%; NVLink remains basically the same; BF16/FP8 peak metrics remain basically unchanged [3][6]&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H200 SXM -&amp;gt; B200 SXM&lt;/td&gt;
					&lt;td&gt;Platform leap from Hopper to Blackwell&lt;/td&gt;
					&lt;td&gt;Memory from 141GB to 180GB, approx +27.7%; memory bandwidth from 4.8 to up to 8 TB/s, approx +66.7%; But the biggest change is FP4, 1.8 TB/s NVLink, and entire system and rack-scale inference efficiency [8][9]&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;B200 SXM -&amp;gt; B300 SXM&lt;/td&gt;
					&lt;td&gt;Blackwell Ultra pushes large memory and reasoning further forward&lt;/td&gt;
					&lt;td&gt;Memory from 180GB to 288GB, approx +60.0%; publicly reported memory bandwidth remains up to 8 TB/s; DGX B300 dense FP4 is 1.5 times better than DGX B200, with attention improving by 2x [10][11]&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Upon reading through, a pattern emerges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;H100 is the generation that aggressively boosted single-card tensor computing power.&lt;/li&gt;
&lt;li&gt;H200 is the generation focused on supplementing VRAM capacity.&lt;/li&gt;
&lt;li&gt;B200 is the generation that transformed &amp;ldquo;training cards&amp;rdquo; into &amp;ldquo;AI factory infrastructure.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;B300 is the generation that more clearly pushes Blackwell towards reasoning and large-scale inference.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;china-exclusive-line&#34;&gt;China Exclusive Line
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Generational Gap&lt;/th&gt;
					&lt;th&gt;At first glance seems like an upgrade, but actually needs to be viewed separately&lt;/th&gt;
					&lt;th&gt;My judgment&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;A800 -&amp;gt; H800&lt;/td&gt;
					&lt;td&gt;If only looking at local HBM bandwidth, from the A100 generation to the H100 generation, this can roughly be understood as a +64% generational advancement.&lt;/td&gt;
					&lt;td&gt;But the core constraint is still interconnection, not single-card local memory.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;H800 -&amp;gt; H20&lt;/td&gt;
					&lt;td&gt;Memory increased from 80GB to 96GB, approx. +20%; if based on common public parameters, bandwidth increased from 3.35 to 4.0 TB/s, approx. +19.4%.&lt;/td&gt;
					&lt;td&gt;This is not a pure upgrade. H20 is a compromise product due to greater compliance pressure and cannot simply be treated as an &amp;ldquo;H800 Plus.&amp;rdquo;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This also explains why the China special supply line is not suitable to frame as &amp;ldquo;how much every generation comprehensively improves.&amp;rdquo; This specific product line inherently carries compliance constraints; its design objective is not technical optimization, but commercial feasibility within regulatory constraints.&lt;/p&gt;
&lt;h2 id=&#34;how-much-has-the-price-actually-increased&#34;&gt;How Much Has the Price Actually Increased?
&lt;/h2&gt;&lt;p&gt;This section is most susceptible to inaccuracies/fabrication. Because NVIDIA rarely publicly discloses the single-card MSRP for data center GPUs, what is more commonly available in the public domain is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DGX complete system price or third-party listed price for the entire system.&lt;/li&gt;
&lt;li&gt;Channel quotes for China-special edition cards/GPUs.&lt;/li&gt;
&lt;li&gt;Media, securities firm, or supply chain news.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Therefore, here I only provide &amp;ldquo;publicly traceable price samples,&amp;rdquo; rather than fabricating an official price list that looks complete but is actually inconsistent in its scope/criteria.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Object&lt;/th&gt;
					&lt;th&gt;Public Price Sample&lt;/th&gt;
					&lt;th&gt;Interpretation compared to previous generation&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;Official starting price of $199,000 at release on 2022-03-22 [4]&lt;/td&gt;
					&lt;td&gt;This is the cleanest official anchor point.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DGX H100&lt;/td&gt;
					&lt;td&gt;Market listing&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So, concerning &amp;ldquo;how much the overall selling price increased,&amp;rdquo; I offer two conclusions:&lt;/p&gt;
&lt;p&gt;First, the global flagship line has indeed risen significantly. Based on publicly comparable samples, the DGX B200 is roughly 40% to 50% more expensive compared to the DGX H100 listed during the same period. [19]&lt;/p&gt;
&lt;p&gt;Second, China&amp;rsquo;s special allocation lines are not characterized by continuous price increases; rather, there might be a situation where &amp;ldquo;later released cards are cheaper.&amp;rdquo; The open quote price for the H20 eight-card server is approximately 30% lower than the H800 eight-card server. The reason is not ethical consideration (or conscience), but that the performance capability has been further compressed. [17]&lt;/p&gt;
&lt;h2 id=&#34;final-wrap-up&#34;&gt;Final Wrap-up
&lt;/h2&gt;&lt;p&gt;If I had to summarize the shifts in NVIDIA data center GPUs following the release of ChatGPT into a single sentence, my take is that:&lt;/p&gt;
&lt;p&gt;The H100 was the catalyst for the generative AI boom; the H200 is a memory-oriented refresh, but the B200 is the true generational leap for the AI factory era, while the B300 clearly paves the way for the reasoning era. The China-specific lineup operates on an entirely different logic. It is not chasing flagships, but rather focusing on maintaining usability within regulatory gaps.&lt;/p&gt;
&lt;p&gt;Do not view these two criteria together/conflate them. If you mix them, it is easy to draw conclusions such as &amp;ldquo;the new card has larger VRAM, therefore its generation is stronger,&amp;rdquo; or &amp;ldquo;the price is lower, therefore the cost-performance ratio is higher.&amp;rdquo; These are conclusions that seem superficially plausible but are based on incorrect premises.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;h2 id=&#34;authors-notes&#34;&gt;Author&amp;rsquo;s Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Compile a summary of NVIDIA GPU models and their corresponding performance parameters released since the launch of ChatGPT, detailing how much each generation upgraded from the previous one and the overall price increase. I specifically need data center GPUs, including special versions for China.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;summary-of-writing-ideas&#34;&gt;Summary of Writing Ideas
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Fix the actual release date of ChatGPT to November 30, 2022, to avoid misalignment of timelines from the start.&lt;/li&gt;
&lt;li&gt;Separate Nvidia data center GPUs into &amp;ldquo;Global Flagship Mainline&amp;rdquo; and &amp;ldquo;China Customized Line,&amp;rdquo; and do not force these two lines into a single generational upgrade history.&lt;/li&gt;
&lt;li&gt;Percentage upgrades should only be calculated for directly comparable metrics, primarily VRAM, memory bandwidth, and interconnects.&lt;/li&gt;
&lt;li&gt;For pricing, do not fabricate MSRP for individual cards; instead, only adopt official starting prices, complete system list prices, and Reuters channel quotes.&lt;/li&gt;
&lt;li&gt;L4, L40S, and L2 are not expanded upon because they tend to mix the training mainline with the general inference/graphics line.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;brainstorming-expansion&#34;&gt;Brainstorming Expansion
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Area&lt;/th&gt;
					&lt;th&gt;Inclusion Status in Main Text&lt;/th&gt;
					&lt;th&gt;Rationale&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;A100 as baseline&lt;/td&gt;
					&lt;td&gt;Included&lt;/td&gt;
					&lt;td&gt;The user asks for &amp;ldquo;previous generation vs. previous,&amp;rdquo; and without A100, it&amp;rsquo;s impossible to calculate the upgrade magnitude of H100.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L4, L40, L40S, L2&lt;/td&gt;
					&lt;td&gt;Rejected&lt;/td&gt;
					&lt;td&gt;Belongs to data center products, but is more oriented towards video/graphics and general inference, inconsistent with the main training price scope.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;GB200, GB300 full system architecture&lt;/td&gt;
					&lt;td&gt;Partially Included&lt;/td&gt;
					&lt;td&gt;Used to explain why Blackwell starts emphasizing platform-level performance rather&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;</description>
        </item>
        <item>
        <title>AI can write code, what will newcomers use to level up?</title>
        <link>https://ttf248.life/en/p/when-ai-writes-code-how-juniors-level-up/</link>
        <pubDate>Mon, 27 Apr 2026 23:18:36 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/when-ai-writes-code-how-juniors-level-up/</guid>
        <description>&lt;p&gt;In the last few months, while writing code using tools like Claude or Codex, my most striking realization wasn&amp;rsquo;t that &amp;ldquo;programmers are obsolete,&amp;rdquo; but rather that many tasks that used to be given to newcomers for practice can now generate a basic first draft themselves. Whether it&amp;rsquo;s creating a scaffold, adding several tests, or making small modifications on the fly—after running through these operations, the speed is genuinely fast, so fast it feels almost bittersweet.&lt;/p&gt;
&lt;p&gt;For someone like me, who graduated ten years ago, frankly, this is more about increasing efficiency. Because I generally know where it&amp;rsquo;s reliable and where it isn&amp;rsquo;t; where something looks functional but actually has pitfalls hidden further down the line. But for fresh graduates, this topic isn&amp;rsquo;t so straightforward. AI isn&amp;rsquo;t just here to take over a few hours of manual labor; it feels more like it is compressing the traditional path of how a newcomer goes from zero knowledge to proficiency. This is also why I wanted to write about it separately.&lt;/p&gt;
&lt;h2 id=&#34;whats-actually-fading-away-isnt-the-programmer-but-mobile-phone-skill-training&#34;&gt;What&amp;rsquo;s actually fading away isn&amp;rsquo;t the programmer, but mobile phone skill training
&lt;/h2&gt;&lt;p&gt;In the past, our team always had a batch of tasks that weren&amp;rsquo;t technically complex but were perfect for new hires. These included modifying pages, integrating interfaces, completing CRUD operations, fixing minor bugs, and debugging by following logs little by little. The work itself was small, but it was enough to take someone from merely &amp;ldquo;knowing how to write code&amp;rdquo; (syntax) to truly understanding &amp;ldquo;how the live system actually works.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Currently, this type of work is the most susceptible to being taken over by AI.&lt;/p&gt;
&lt;p&gt;The U.S. Bureau of Labor Statistics (BLS), in its updated Occupational Outlook for 2025, provided a very interesting comparison. On one hand, it states that roles such as software development and testing will continue to grow over the next decade; but on the other hand, it clearly indicates that the position of computer programmer—which is more focused on &amp;ldquo;writing and executing code&amp;rdquo;—is declining, as many repetitive programming tasks will continue to be automated. This change is critical.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not that the software industry doesn&amp;rsquo;t need people; rather, the value derived solely from &amp;ldquo;taking tasks and writing code&amp;rdquo; is increasingly diminishing. Corporations, of course, are focused on cost efficiency: if AI can generate a first draft, followed by an experienced person to refine it, why do they still need to staff a slew of junior positions merely for slow mentorship, like in the past?&lt;/p&gt;
&lt;p&gt;So, what might be truly frightening may not be layoffs themselves, but often the fact that companies are no longer replenishing staff or expanding roles. The door is still there, but the opening has become narrower.&lt;/p&gt;
&lt;h2 id=&#34;why-seasoned-professionals-are-best-positioned-to-capitalize-on-ai-dividends&#34;&gt;Why Seasoned Professionals are Best Positioned to Capitalize on AI Dividends
&lt;/h2&gt;&lt;p&gt;It’s quite paradoxical. On the surface, code generated by AI might not be better than that written by fresh graduates. However, those who truly master using AI are often seasoned professionals who have learned through many pitfalls and failures.&lt;/p&gt;
&lt;p&gt;The reason is also not complex.&lt;/p&gt;
&lt;p&gt;First, seasoned professionals know that &amp;ldquo;looks runnable&amp;rdquo; is not the same as &amp;ldquo;truly deployable.&amp;rdquo; In Anthropic&amp;rsquo;s Economic Index from January 2026, when they analyzed software development in isolation, they found that although these requests are highly standardized, the task success rate was only about 61%. Furthermore, most scenarios still require back-and-forth iteration; it cannot simply be resolved by dumping the entire thing to AI once. METR conducted an even more sobering experiment in July 2025, allowing open-source maintainers who had been familiar with the repository for years to use cutting-edge AI tools of that time. The result was actually a 19% overall slowdown. This indicates that when the context is complex, quality standards are high, and boundaries are fuzzy, AI is not autonomous driving; it is more like a very talkative intern.&lt;/p&gt;
&lt;p&gt;Secondly, experienced developers have something they call &amp;ldquo;code taste.&amp;rdquo; This concept might sound somewhat abstract, but it is very tangible. Whether an interface should be split this way, whether exceptions should be swallowed like this, whether testing is merely a trick to fool the CI, or if refactoring will preemptively bury pitfalls for the next week—these are things that matter. AI makes mistakes now, and many of those mistakes aren&amp;rsquo;t syntax errors; they are conceptual errors, abstraction errors, or boundary condition errors. Honestly, it is even harder for someone who hasn&amp;rsquo;t experienced traditional programming to identify these types of mistakes.&lt;/p&gt;
&lt;p&gt;So, in the age of AI, what is most valuable is not typing speed, but critical thinking (or judgment).&lt;/p&gt;
&lt;h2 id=&#34;newcomers-arent-without-paths-they-are-just-out-of-the-old-ones&#34;&gt;Newcomers aren&amp;rsquo;t without paths, they are just out of the old ones.
&lt;/h2&gt;&lt;p&gt;I don&amp;rsquo;t believe that newcomers will be without opportunities. The World Economic Forum&amp;rsquo;s January 2025 report still ranks software and application developers among rapidly growing roles, and the U.S. Bureau of Labor Statistics&amp;rsquo; long-term outlook for software development positions also shows growth. This suggests that the demand has not disappeared entirely.&lt;/p&gt;
&lt;p&gt;But the entry method must have changed.&lt;/p&gt;
&lt;p&gt;The previous default path was to start with some grunt or difficult work, learning as you went while coding, and relying on sheer time and effort to build your intuition. Now, companies are more likely to expect that upon joining, you already know two things.&lt;/p&gt;
&lt;p&gt;One thing is to treat AI as a tool, not as an answer. You must be able to clearly articulate the problem, break down the requirements, and prompt it to give you usable drafts first.&lt;/p&gt;
&lt;p&gt;Another thing is being able to review it effectively. It&amp;rsquo;s not enough to just say, &amp;ldquo;This is wrong&amp;rdquo;; rather, you need to know where the mistake lies, why it is wrong, and how to modify it so that it aligns with the project context.&lt;/p&gt;
&lt;p&gt;This is troublesome. Because the ability to &amp;ldquo;review code&amp;rdquo; is something that usually takes years of working experience to develop, now that opportunities to practice are decreasing, yet newcomers are instead required to possess this skill much earlier. How should I put it? It&amp;rsquo;s like a game that dismantled the newbie village, but the boss is still waiting for you up ahead.&lt;/p&gt;
&lt;h2 id=&#34;what-will-it-be-like-in-the-future&#34;&gt;What Will It Be Like in the Future
&lt;/h2&gt;&lt;p&gt;My current judgment/assessment is relatively simple.&lt;/p&gt;
&lt;p&gt;AI will continue to boost the output of experienced professionals while also continuing to diminish those parts of work in entry-level positions that are easiest to standardize and break down. These two things are likely to happen simultaneously, and they do not conflict. The ILO&amp;rsquo;s report from May 2025 was quite measured, suggesting that generative AI is more likely to change the structure of tasks rather than eliminating entire jobs all at once. The issue is that when the task structure changes, the first things to be eliminated are often those low-risk tasks originally designed for training new employees.&lt;/p&gt;
&lt;p&gt;Therefore, the most scarce talent going forward is not merely someone who &amp;ldquo;codes best,&amp;rdquo; but rather someone who possesses foundational abilities, can harness AI, and takes ownership of both business outcomes and quality.&lt;/p&gt;
&lt;p&gt;For seasoned professionals, this feels like adding a sharper shovel—it&amp;rsquo;s still taxing, but significantly more efficient. However, for beginners, the issue isn&amp;rsquo;t whether they should use AI; it’s that if they rely on it from the start, who will tell them when it writes well, and when it is producing convincingly plausible nonsense?&lt;/p&gt;
&lt;p&gt;Neither the school nor AI can provide this answer right now. Ultimately, you&amp;rsquo;ll probably have to figure it out yourself. Ugh, the door isn&amp;rsquo;t closed, but getting through it is much harder.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompts&#34;&gt;Original Prompts
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Some thoughts on AI programming. As a recent graduate, my coding abilities are certainly not comparable to Claude or Codex. For experienced developers, AI can boost efficiency, but it has also led companies to significantly cut the positions of junior programmers. I graduated ten years ago and have gone through the process of starting as a beginner and reaching senior levels. What about those who enter the field now? How will they fare? The older generation lived through the days of &amp;ldquo;old-school&amp;rdquo; programming, so frankly speaking, they can recognize whether AI wrote good code or bad code, because current AIs still make mistakes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;summary-of-writing-ideas&#34;&gt;Summary of Writing Ideas
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Center the main argument on the idea that &amp;ldquo;AI is compressing the learning ground for newcomers, not shrinking the entire software industry.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;In the first half, first discuss why experienced workers benefit more from the AI dividends, and then explain how this can coexist with newcomer anxiety.&lt;/li&gt;
&lt;li&gt;Use materials/data sets like BLS, WEF, Anthropic, METR, and ILO to firmly establish two points: &amp;ldquo;structural job changes&amp;rdquo; and &amp;ldquo;the necessity of human judgment in AI.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Intentionally avoid elaborating on specific job search strategies or creating a &amp;lsquo;how newcomers should learn&amp;rsquo; list of motivational advice.&lt;/li&gt;
&lt;li&gt;The conclusion must return to the core reality: it is not that there are no opportunities, but rather that the traditional &amp;ldquo;newbie training ground&amp;rdquo; is disappearing.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>After moving to Unicom, the US nodes aren&#39;t as good anymore.</title>
        <link>https://ttf248.life/en/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/en/p/us-nodes-not-as-good-after-switching-to-china-unicom/</guid>
        <description>&lt;p&gt;I recently moved, and the broadband at my place switched from China Telecom to Unicom. Usually, when I watch dramas or play games, I don&amp;rsquo;t feel any noticeable difference. It wasn&amp;rsquo;t until a couple of days ago that I tried to download some materials and habitually switched to the US node, but no matter what, the speed wouldn&amp;rsquo;t go up. I was a bit dumbfounded at the time.&lt;/p&gt;
&lt;p&gt;I figured this out later. I used to think that because the bandwidth in US data centers was sufficient, the US nodes were naturally stronger. Now I see that this understanding is only half right. Having ample server resources in the US is one thing; which broadband provider to use domestically, how the international exit path works, and whether the return trip benefits from better backbone connections are the other half. Issues that weren&amp;rsquo;t exposed when I used Telecom were all revealed after switching to Unicom.&lt;/p&gt;
&lt;h2 id=&#34;its-really-hard-to-notice-in-daily-life&#34;&gt;It&amp;rsquo;s really hard to notice in daily life
&lt;/h2&gt;&lt;p&gt;These kinds of differences are easily hidden in normal circumstances.&lt;/p&gt;
&lt;p&gt;Watching shows relies on the video platform&amp;rsquo;s own scheduling, and often cross-border bandwidth doesn&amp;rsquo;t need to be kept at full capacity. Gaming is more concerned with latency jitter and node stability; when it comes to downloading large files or running high bandwidth continuously, whether the line is suitable immediately reveals its flaws.&lt;/p&gt;
&lt;p&gt;So now I actually feel that the download speed is the most honest benchmark. Just because you usually feel everything is normal doesn&amp;rsquo;t mean this connection is truly wide; it just means you haven&amp;rsquo;t pushed it to its limit.&lt;/p&gt;
&lt;h2 id=&#34;previously-us-nodes-could-be-fully-utilized-its-not-just-the-us-data-centers-that-are-powerful&#34;&gt;Previously, US nodes could be fully utilized; it&amp;rsquo;s not just the US data centers that are powerful.
&lt;/h2&gt;&lt;p&gt;In the publicly available materials from China Telecom, ChinaNet is its main network, and &lt;code&gt;CN2 (AS4809)&lt;/code&gt; is its next-generation global backbone network that emphasizes low latency and high quality, targeting businesses that place greater importance on international quality. Many people have heard of this; even airport vendors often use it as a selling point.&lt;/p&gt;
&lt;p&gt;So, previously when I was on the telecom broadband, switching to a US node could boost my download speed. Looking back now, it&amp;rsquo;s probably not that &amp;ldquo;America is inherently faster,&amp;rdquo; but rather that the combination of &amp;ldquo;US data center resources + upstream lines at the node + China Telecom&amp;rsquo;s international routing&amp;rdquo; just happened to align perfectly. Once the line hits a direction favorable to China Telecom, the experience really is great.&lt;/p&gt;
&lt;p&gt;To put it plainly, at that speed before, it wasn&amp;rsquo;t necessarily that the nodes were generally stronger; it was more like I happened to be standing on the side that had an advantage.&lt;/p&gt;
&lt;h2 id=&#34;china-unicom-doesnt-lack-international-internet-but-the-route-might-not-be-the-same-as-your-original-one&#34;&gt;China Unicom doesn&amp;rsquo;t lack international internet, but the route might not be the same as your original one.
&lt;/h2&gt;&lt;p&gt;However, this matter cannot be simply understood as &amp;ldquo;China Unicom relies entirely on renting telecom networks.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;China Unicom also has a considerable international network. The official website of China Unicom clearly states that its backbone network, &lt;code&gt;AS4837&lt;/code&gt;, has over 400 PoPs globally, and it also has &lt;code&gt;AS9929&lt;/code&gt; for higher quality capacity. In terms of coverage, it has laid out points in Singapore, Taiwan, and the United States.&lt;/p&gt;
&lt;p&gt;But the problem is this. &amp;ldquo;&lt;code&gt;Having international internet access&lt;/code&gt;&amp;rdquo; and &amp;ldquo;&lt;code&gt;the proxy node you bought right now happens to be friendly to this carrier&lt;/code&gt;&amp;rdquo; are not the same thing.&lt;/p&gt;
&lt;p&gt;What home broadband users truly feel is not just whether the carrier has overseas resources, but also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which carrier is local to you?&lt;/li&gt;
&lt;li&gt;Which return path does the proxy service provider&amp;rsquo;s upstream prefer?&lt;/li&gt;
&lt;li&gt;Are the outbound and return paths of the nodes optimized in the same way?&lt;/li&gt;
&lt;li&gt;Is there congestion during peak hours?&lt;/li&gt;
&lt;li&gt;Is this link more biased towards China Telecom, or more biased towards Unicom/China Mobile?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many US nodes; in the past, I could saturate them when using Telecom, but it doesn&amp;rsquo;t work after switching to Unicom. I am more inclined to understand it as: the line combination of these nodes was inherently more friendly to Telecom. I didn&amp;rsquo;t feel this before because I myself was on Telecom.&lt;/p&gt;
&lt;h2 id=&#34;why-singapore-and-taiwan-are-doing-better&#34;&gt;Why Singapore and Taiwan are Doing Better
&lt;/h2&gt;&lt;p&gt;This is also quite interesting.&lt;/p&gt;
&lt;p&gt;After switching to Unicom, the US nodes are no longer working well, but Singapore and Taiwan have improved quite a bit. This change actually illustrates the issue better than just saying &amp;ldquo;US node speed dropped.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;My understanding is that the Asia direction was originally closer, with shorter paths, and denser regional interconnection. Since Connect has network resources and PoPs in both Singapore and Taiwan, if the proxy service provider provides a more direct regional path at the Asian nodes, the download speed will naturally be easier to achieve.&lt;/p&gt;
&lt;p&gt;This doesn&amp;rsquo;t necessarily mean that Singapore or Taiwan data centers are stronger than the US, nor does it mean that all broadband connections should choose an Asian node. It&amp;rsquo;s more like your current broadband has a better line match with these regional nodes.&lt;/p&gt;
&lt;p&gt;How should I put it? Whether a node is strong or not isn&amp;rsquo;t just about its physical location in the data center; you also have to consider if the route home recognizes you.&lt;/p&gt;
&lt;h2 id=&#34;this-finally-corrected-my-previous-understanding&#34;&gt;This finally corrected my previous understanding
&lt;/h2&gt;&lt;p&gt;My previous thought was quite simple: US data centers are large, and broadband resources are abundant, so choosing a US node for downloading materials should basically never be wrong.&lt;/p&gt;
&lt;p&gt;Looking at it now, this judgment is too coarse.&lt;/p&gt;
&lt;p&gt;A more accurate way to say it is:&lt;/p&gt;
&lt;p&gt;Whether the US node can be fully utilized depends not only on the US data center, but also on which backbone network your broadband provider is connected to.&lt;/p&gt;
&lt;p&gt;The line that works very well for China Telecom users might not work the same way when switched to Unicom. For Singapore and Taiwan, which are actually smoother under Unicom, it&amp;rsquo;s not that &amp;ldquo;Asian nodes suddenly got stronger,&amp;rdquo; but rather that the lines finally fit better.&lt;/p&gt;
&lt;p&gt;So in the future, when buying proxies or choosing nodes, I probably won&amp;rsquo;t automatically focus on the US anymore. It&amp;rsquo;s more important to first look at what kind of broadband you have and then see which line the service provider favors, rather than whether the data center is in the US or Asia.&lt;/p&gt;
&lt;p&gt;Ah, this kind of difference is really hard to tell when just binge-watching dramas or playing games. It only becomes obvious when you have to transfer large files.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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 Introduction)&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 Introduction)&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 (Overseas Nodes in Singapore, Taiwan, USA, etc.)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Prompt: In mainland China, when using proxy software for scientific internet access, I noticed a difference between Unicom and Telecom broadband. It&amp;rsquo;s hard to notice during daily activities like watching dramas or gaming. Occasionally downloading materials, I prefer selecting US nodes because the download speed there can basically reach full capacity. This aligns with my previous understanding that servers in US data centers are provided with ample broadband resources. Recently, after moving and switching to Unicom bandwidth, I discovered a problem: the download speed on US nodes cannot go up anymore. Switching to Singapore or Taiwan has improved things a lot. This is related to China&amp;rsquo;s backbone network; CN2 high-speed backbone networks are all operated by Telecom, while Unicom has to rent Telecom&amp;rsquo;s network.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-outline-summary&#34;&gt;Writing Outline Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Keep the real trigger point of &amp;ldquo;only realizing the difference after moving and changing broadband&amp;rdquo; and do not write the article as a general network science popularization piece.&lt;/li&gt;
&lt;li&gt;Keep the core judgment: &amp;ldquo;It&amp;rsquo;s about how fast the node is, and you can&amp;rsquo;t just look at US data centers; you also need to consider domestic broadband and international routing.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Supplemented verification on &lt;code&gt;CN2&lt;/code&gt;, China Unicom international backbone, and overseas PoPs, correcting &amp;ldquo;China Unicom relies entirely on renting Telecom network&amp;rdquo; to a more accurate description.&lt;/li&gt;
&lt;li&gt;For the part about Singapore and Taiwan being faster, explain it as &amp;ldquo;shorter regional paths and higher line matching degree,&amp;rdquo; rather than stating it as an absolute rule.&lt;/li&gt;
&lt;li&gt;Structurally, first write the subjective feeling (experience), then explain the reasons, and finally bring the conclusion back to &amp;ldquo;how to choose nodes in the future.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Changing the architecture, Hermes and OpenClaw tokens are not consumed in the same way.</title>
        <link>https://ttf248.life/en/p/hermes-openclaw-token-usage-diff/</link>
        <pubDate>Thu, 16 Apr 2026 01:13:53 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/hermes-openclaw-token-usage-diff/</guid>
        <description>&lt;p&gt;After finishing the article &amp;ldquo;[Mistaking Hermes for an OpenClaw Alternative, Maybe I Was Biased From the Start]&amp;quot;(/en/p/hermes-openclaw-not-the-same-game/), I went through a round of documentation on both sides. The more I read, the more I felt that to truly see the difference between these two things, just looking at the features isn&amp;rsquo;t enough; looking at how tokens are consumed is actually more direct.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll state my judgment first.&lt;/p&gt;
&lt;p&gt;OpenClaw is by default more like a long-term online workbench; many identities, rules, workspace files, and message constraints naturally persist across conversation rounds, so the base model is usually heavier. Hermes, on the other hand, is noticeably more restrained; much of the context is discovered and injected on demand, and the system prompt deliberately maintains a stable prefix, making it easier to control token usage by default.&lt;/p&gt;
&lt;p&gt;Of course, this doesn&amp;rsquo;t mean that Hermes is necessarily more cost-effective. If you enable the memory provider, skills, sub-agent, and long tool output all at once, it can burn through tokens just as fast. But frankly speaking, these two architectures have not been consuming tokens in the same way since day one.&lt;/p&gt;
&lt;h2 id=&#34;why-is-openclaw-by-default-heavier&#34;&gt;Why is OpenClaw by default heavier?
&lt;/h2&gt;&lt;p&gt;OpenClaw&amp;rsquo;s design logic was never &amp;ldquo;to create a lightweight agent for a quick chat,&amp;rdquo; but rather &amp;ldquo;to build up a long-term, persistent agent workbench.&amp;rdquo; This is stated very clearly in the official workspace documentation.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;SOUL.md&lt;/code&gt;, and &lt;code&gt;USER.md&lt;/code&gt; are loaded for every session. These files, such as &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;, and &lt;code&gt;MEMORY.md&lt;/code&gt;, also revolve around the same workspace. The documentation even specifically reminds that &lt;code&gt;HEARTBEAT.md&lt;/code&gt; should be kept very short to avoid token burn. This reminder itself indicates that OpenClaw is well aware that its default context is quite thick.&lt;/p&gt;
&lt;p&gt;However, one thing that needs to be made clear is that OpenClaw doesn&amp;rsquo;t have everything permanently resident in the context. Its official token documentation clearly states that when skills are added to the system prompt, they are by default just metadata, and specific instructions must be explicitly &lt;code&gt;read&lt;/code&gt; as needed. Therefore, its issue isn&amp;rsquo;t &amp;ldquo;not knowing how to save,&amp;rdquo; but rather that &amp;ldquo;the base it defaults to carrying is inherently larger.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This is actually not difficult to understand. OpenClaw aims to solve the issues of &amp;ldquo;long-term online presence&amp;rdquo; and &amp;ldquo;multi-message surface visibility.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;You have to make an agent live simultaneously in places like Telegram, Discord, Slack, and WhatsApp. It also needs identity, routing, boundaries, and delegation. Many rules cannot just be tacked on temporarily. It first needs to know who it is, how to speak, whom it&amp;rsquo;s facing, what the constraints of the current workspace are, where to find skills, which memories to carry, and which memories not to carry.&lt;/p&gt;
&lt;p&gt;So, the token consumption for OpenClaw is more like a large fixed overhead.&lt;/p&gt;
&lt;p&gt;Every time you send a message, you are not just saying one sentence to a model; you are activating an entire set of pre-configured &amp;ldquo;assistant environment.&amp;rdquo; This environment is very useful, but the cost is that the base prompt is thicker, and much context, even if not directly used in this round, will be placed there first.&lt;/p&gt;
&lt;h2 id=&#34;why-hermes-looks-more-restrained&#34;&gt;Why Hermes Looks More Restrained
&lt;/h2&gt;&lt;p&gt;Regarding Hermes, the most interesting part of the documentation is that it keeps emphasizing two things: on-demand loading and prompt cache preservation.&lt;/p&gt;
&lt;p&gt;First, look at the context files. Hermes only loads the project contexts that match the current working directory when a session starts; things like &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;CLAUDE.md&lt;/code&gt;, and &lt;code&gt;.cursorrules&lt;/code&gt; are first-match wins, so they won&amp;rsquo;t all be loaded at once. More importantly, the &lt;code&gt;AGENTS.md&lt;/code&gt; in a subdirectory isn&amp;rsquo;t read entirely at startup; it is discovered and injected incrementally only when you actually navigate to that directory, read that file, or reach that path.&lt;/p&gt;
&lt;p&gt;The official documentation clearly writes out the benefits of this design:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;no system prompt bloat&lt;/li&gt;
&lt;li&gt;prompt cache preservation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This taste is very different from OpenClaw. Hermes seems to be saying that it&amp;rsquo;s best not to make the system prompt too long; place context that can be deferred, and don&amp;rsquo;t fill up the first round with things that only appear at relevant times.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;skills&lt;/code&gt; also follow the same logic. The Hermes &lt;code&gt;skills&lt;/code&gt; documentation explicitly mentions progressive disclosure, with the goal of minimizing token usage. In other words, the skill is not permanently present in the full text by default; instead, the model first sees a lightweight index and only expands to show the detailed content when it&amp;rsquo;s truly necessary.&lt;/p&gt;
&lt;p&gt;Furthermore, its memory system is also combinatorial. The official documentation severely limits the built-in memory: &lt;code&gt;MEMORY.md&lt;/code&gt; is about 800 tokens, and &lt;code&gt;USER.md&lt;/code&gt; is about 500 tokens, totaling a fixed capacity of around 1300 tokens. &lt;code&gt;SOUL.md&lt;/code&gt; is for fixed identity, while &lt;code&gt;USER.md&lt;/code&gt; and memory are included in the system prompt, but items like session search, memory provider, and Honcho feel more like external layers. This structure doesn&amp;rsquo;t naturally shorten the prompt, but it provides a default posture that is easier to control cost-wise.&lt;/p&gt;
&lt;p&gt;So, from an architectural perspective, I would be more inclined to categorize Hermes as &amp;ldquo;default restraint, gradually increasing weight.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;its-not-about-who-is-more-advanced-but-where-the-cost-is-placed&#34;&gt;It&amp;rsquo;s not about who is more advanced, but where the cost is placed
&lt;/h2&gt;&lt;p&gt;Many comparison articles like to write token consumption as a single conclusion, which I think is incorrect.&lt;/p&gt;
&lt;p&gt;OpenClaw is heavier, but that doesn&amp;rsquo;t mean it&amp;rsquo;s poorly designed. On the contrary, it intentionally front-loads a lot of features. If you want an assistant that is long-term online, cross-platform, has personality, has a workspace, and has routing, then these contexts will eventually cost something. It just chooses to bring all these things together on the default path first.&lt;/p&gt;
&lt;p&gt;Hermes is more restrained, but that doesn&amp;rsquo;t mean it has no overhead. Once you stack a long &lt;code&gt;SOUL.md&lt;/code&gt;, the project &lt;code&gt;AGENTS.md&lt;/code&gt;, multiple skills, MCP, memory provider, sub-agent, and long tool outputs together, tokens can still roll very quickly. Especially if the tool output itself is very long, or if you make it search back and forth in a complex codebase, that&amp;rsquo;s not something you can save just by naming the architecture.&lt;/p&gt;
&lt;p&gt;So, a more accurate way to say it is:&lt;/p&gt;
&lt;p&gt;OpenClaw moves the cost to &amp;ldquo;assistant environment residency.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Hermes shifts the cost to &amp;ldquo;capability on demand.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Neither approach is absolutely superior, but their billing structures are completely different.&lt;/p&gt;
&lt;h2 id=&#34;what-truly-makes-the-difference-is-not-just-the-system-prompt&#34;&gt;What truly makes the difference is not just the system prompt
&lt;/h2&gt;&lt;p&gt;There are a few details that are quite easy to overlook.&lt;/p&gt;
&lt;p&gt;First, Hermes was designed to stabilize the prefix in the context files. The subdirectory hints are appended to the results of relevant tools, rather than continuously feeding all project contexts into the system prompt. This approach not only saves tokens but also essentially leaves space for the provider&amp;rsquo;s prompt cache.&lt;/p&gt;
&lt;p&gt;Second, tools like Hermes&amp;rsquo;s &lt;code&gt;execute_code&lt;/code&gt; are designed to only return the final result printed by the script back to the model; all the intermediate results from RPC tool calls will not be included in the context. In complex workflows, this structure can eliminate a large amount of meaningless tool noise.&lt;/p&gt;
&lt;p&gt;Third, OpenClaw&amp;rsquo;s workspace philosophy means it&amp;rsquo;s more like &amp;ldquo;bringing everything with you.&amp;rdquo; Even if many individual files are short, as long as there are many types of files, many layers of persona, many layers of rules, and many levels of skills and memories, the foundational context will gradually become thicker. This thickness is not a bug; it is part of its product design trade-offs.&lt;/p&gt;
&lt;p&gt;Fourth, how both sides handle the sub-agent will also affect the total ledger. Many of Hermes&amp;rsquo; designs emphasize local context and local execution, while OpenClaw emphasizes the organizational capabilities of multiple agent identities, routes, and delegates. One is more like switching context, and the other is more like organizing existence relationships. Finally, when it comes to the token billing, the shape is naturally different.&lt;/p&gt;
&lt;h2 id=&#34;if-you-want-to-calculate-it-dont-rely-on-your-mouth&#34;&gt;If you want to calculate it, don&amp;rsquo;t rely on your mouth
&lt;/h2&gt;&lt;p&gt;If you are really planning to use it long-term, don&amp;rsquo;t just listen to anyone saying &amp;ldquo;this is more energy efficient&amp;rdquo;; look directly at the data provided by the tool.&lt;/p&gt;
&lt;p&gt;OpenClaw has commands like &lt;code&gt;/context detail&lt;/code&gt; and &lt;code&gt;/usage tokens&lt;/code&gt;, and the documentation will also remind you which files are injected at the start of each session. This is very suitable for seeing how thick the base knowledge really is.&lt;/p&gt;
&lt;p&gt;Regarding Hermes, the official documentation provides clues such as token budget, skills progressive disclosure, and context file injection methods in Honcho and related features. You can look at this by combining &lt;code&gt;hermes insights&lt;/code&gt;, session storage, and actual provider billing.&lt;/p&gt;
&lt;p&gt;To put it plainly, the architecture can only determine &amp;ldquo;how to spend,&amp;rdquo; but it cannot decide &amp;ldquo;how much to spend.&amp;rdquo; What truly determines the bill is how long your &lt;code&gt;SOUL.md&lt;/code&gt; is, how much memory you&amp;rsquo;ve stuffed in, how many skills you&amp;rsquo;ve enabled, whether the tool output was contained, and how expensive the model itself is.&lt;/p&gt;
&lt;h2 id=&#34;im-still-on-this-side&#34;&gt;I&amp;rsquo;m still on this side
&lt;/h2&gt;&lt;p&gt;If I only look at the default architecture and not extreme configurations, I still tend to think that Hermes is better at keeping token consumption within a relatively restrained range.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not because it&amp;rsquo;s stronger, but because from the start of prompt design, it has been avoiding &amp;ldquo;meaningless persistent context bloat.&amp;rdquo; OpenClaw is the opposite; it would rather carry a bit more to ensure that the identity, workspace, and message boundary of that long-term online assistant don&amp;rsquo;t fall apart.&lt;/p&gt;
&lt;p&gt;So if you are particularly concerned about token costs, and the workflow mainly happens in local, CLI, code repository, or skill knowledge base scenarios, Hermes&amp;rsquo; approach will generally be more convenient.&lt;/p&gt;
&lt;p&gt;If you want a long-term assistant that is truly available across various messaging platforms, then spending more tokens with OpenClaw feels like a normal cost. You can&amp;rsquo;t expect it to be online long-term like a person while also requiring it to be as lightweight as a one-off tool every turn.&lt;/p&gt;
&lt;p&gt;Ah, this matter ultimately comes back to the old question.&lt;/p&gt;
&lt;p&gt;Are you raising an online assistant, or are you raising a local agent core? Figure that out, and the token accounting won&amp;rsquo;t be so hard to understand.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/hermes-openclaw-not-the-same-game/&#34; &gt;Previous Post: Mistaking Hermes for OpenClaw Replacement, Might Have Been Biased From the Start&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Prompt: Hermes and OpenClaw write another article about their token consumption. Since the architectures are different, the consumption must be different.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-idea-summary&#34;&gt;Writing Idea Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Continue the judgment from the previous article, but narrow the focus to token consumption, and no longer repeat the overall architecture comparison.&lt;/li&gt;
&lt;li&gt;Focus on comparing the default context assembly methods of both sides, rather than just talking about &amp;ldquo;who is more efficient.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Hermes focuses on progressive disclosure, on-demand discovery, and prompt cache preservation.&lt;/li&gt;
&lt;li&gt;OpenClaw focuses on workspace persistent files, long-term online assistant environments, and fixed overhead.&lt;/li&gt;
&lt;li&gt;The conclusion retains the core judgment provided by the user, but adds a layer of practical reminder: to truly see the bill, one must combine official usage/context tools with actual provider billing.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Treating Hermes as a replacement for OpenClaw might be biased from the start.</title>
        <link>https://ttf248.life/en/p/hermes-openclaw-not-the-same-game/</link>
        <pubDate>Wed, 15 Apr 2026 23:52:20 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/hermes-openclaw-not-the-same-game/</guid>
        <description>&lt;p&gt;Over the past couple of days, I&amp;rsquo;ve been flipping back and forth through the documentation for Hermes and OpenClaw, and the more I read, the more I feel that many people compare these two projects as if they are on the same level; in reality, the comparison is biased from the start.&lt;/p&gt;
&lt;p&gt;They are all doing &amp;ldquo;personal AI assistants.&amp;rdquo; They can receive messages, call models, run tools, and retain some context. Hermes even specifically created &lt;code&gt;hermes claw migrate&lt;/code&gt;, clearly knowing it would receive a batch of OpenClaw users.&lt;/p&gt;
&lt;p&gt;But frankly speaking, Hermes is not a skin-deep version of OpenClaw, and OpenClaw is not just an agent framework with a few extra message entry points. One grows outward from the &lt;code&gt;Gateway&lt;/code&gt;, while the other grows outward from the &lt;code&gt;AIAgent&lt;/code&gt;. If you don&amp;rsquo;t grasp this difference first, discussing architecture, design philosophy, and ecosystem later will just lead to more confusion.&lt;/p&gt;
&lt;h2 id=&#34;they-were-never-intended-to-become-one-thing&#34;&gt;They were never intended to become one thing
&lt;/h2&gt;&lt;p&gt;The OpenClaw official documentation places &lt;code&gt;Gateway&lt;/code&gt; in a very prominent position. Its definition is quite direct: a long-running Gateway holds all message planes, and the control plane clients, Web UI, and Node devices all connect to this Gateway via WebSocket. Furthermore, only one Gateway on a host manages the core message connections; sessions, like those used by WhatsApp, are explicitly monopolized by it.&lt;/p&gt;
&lt;p&gt;What does this mean? It means that OpenClaw&amp;rsquo;s worldview is not &amp;ldquo;first have an agent, then connect several entry points to it,&amp;rdquo; but rather &amp;ldquo;first have a persistent communication and control plane, and then organize the agent runtime, session, skills, delegate, and sub-agent within this plane.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Hermes is not like this. Its architecture diagram clearly shows these entry points: CLI, Gateway, ACP, Batch Runner, API Server, and Python Library, all converging into the same &lt;code&gt;AIAgent&lt;/code&gt; core. Session storage uses SQLite + FTS5, and the tool backend, provider resolution, and prompt builder are all organized around this core.&lt;/p&gt;
&lt;p&gt;So, in terms of architecture, OpenClaw is more like a &amp;ldquo;resident personal assistant system&amp;rdquo;; Hermes is more like an &amp;ldquo;agent runtime platform,&amp;rdquo; and the messaging entry point is just one extension surface of it.&lt;/p&gt;
&lt;h2 id=&#34;one-that-treats-the-gateway-as-the-protagonist-and-one-that-treats-the-agent-as-the-protagonist&#34;&gt;One that treats the Gateway as the protagonist, and one that treats the Agent as the protagonist
&lt;/h2&gt;&lt;p&gt;This architectural difference will ultimately translate into a difference in user experience.&lt;/p&gt;
&lt;p&gt;OpenClaw&amp;rsquo;s strength is in making the concept of &amp;ldquo;how to have a long-term, identifiable, bounded, and routable assistant across many channels&amp;rdquo; very robust. It has default DM pairing, security policies, routing by channel/account/peer, multi-agent binding, delegate mode, and even organizational-level issues like &amp;ldquo;who sends on behalf of, which account receives, and where it appears in the group&amp;rdquo; are specifically documented.&lt;/p&gt;
&lt;p&gt;In other words, what OpenClaw wants to solve first is the existence problem. This agent must first act like a real person, or like a real organizational assistant, living on platforms such as Telegram, Discord, Slack, WhatsApp, Signal, and WebChat. Only then can you talk about whether it will write code or perform task distribution.&lt;/p&gt;
&lt;p&gt;Hermes&amp;rsquo; focus is more on &amp;ldquo;capability accumulation.&amp;rdquo; While it certainly has Gateway and can connect to platforms like Telegram, Discord, Slack, and WhatsApp, the most prominent terms in its official narrative are not routing or accounts, but persistent memory, skills, MCP, sub-agents, toolsets, as well as batch processing, trajectory export, and RL training.&lt;/p&gt;
&lt;p&gt;This is very crucial. The core problem Hermes needs to solve is not &amp;ldquo;how to connect an AI to as many messaging platforms as possible,&amp;rdquo; but rather &amp;ldquo;how to let this agent gradually develop its own skills, memory, and reusable capabilities, and even be able to conveniently use them for training data and experiments.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So I would prefer to summarize it like this:&lt;/p&gt;
&lt;p&gt;OpenClaw is a communication-first personal assistant system.&lt;/p&gt;
&lt;p&gt;Hermes is an agent-core-first self-hosted agent platform.&lt;/p&gt;
&lt;h2 id=&#34;memory-skills-and-ways-of-extension-its-not-one-flavor&#34;&gt;Memory, Skills, and Ways of Extension, It&amp;rsquo;s Not One Flavor
&lt;/h2&gt;&lt;p&gt;Many people will say, don&amp;rsquo;t both support skill, memory, and plugin? That&amp;rsquo;s right, they both do. But the focus is different.&lt;/p&gt;
&lt;p&gt;In OpenClaw&amp;rsquo;s agent runtime, the workspace is a very heavy concept. Files like &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;, and &lt;code&gt;USER.md&lt;/code&gt; are directly injected into the context in the first round of a new session. Skill loading also has an entire set of priorities: those in the workspace, those in the project, those in the personal directory, those in &lt;code&gt;~/.openclaw/skills&lt;/code&gt;, and bundled ones can all stack up. Essentially, it is building a unified long-term assistant environment of &amp;ldquo;personality + rules + workspace + channel.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Hermes&amp;rsquo;s skills system is clearly more geared towards &amp;ldquo;procedural memory.&amp;rdquo; The official documentation states that skills are on-demand knowledge documents, following progressive disclosure, and are compatible with the open standard of &lt;code&gt;agentskills.io&lt;/code&gt;, all unified under &lt;code&gt;~/.hermes/skills/&lt;/code&gt;. Furthermore, the agent itself can modify and delete skills. This feels less like &amp;ldquo;giving the assistant a few instruction manuals&amp;rdquo; and more like &amp;ldquo;crystallizing past actions into reusable capability blocks.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Memory is the same. Hermes has built-in, very restrained &lt;code&gt;MEMORY.md&lt;/code&gt; and &lt;code&gt;USER.md&lt;/code&gt;, plus a session search using SQLite + FTS5, and it also supports 8 types of external memory providers. This design is quite interesting; instead of writing &amp;ldquo;long-term personality environment&amp;rdquo; in great detail right away, it separates short-term memory, retrieval-based recall, and external long-term memory.&lt;/p&gt;
&lt;p&gt;OpenClaw is not without memory, nor is it without advanced capabilities like sub-agents, multi-agent systems, or delegation. On the contrary, it has plenty of these. However, based on its documentation structure and default mental model, it feels more like managing a continuously online assistant workspace; Hermes feels more like managing a continually evolving agent capability graph.&lt;/p&gt;
&lt;h2 id=&#34;why-is-the-ecosystem-drifting-further-and-further-apart&#34;&gt;Why is the ecosystem drifting further and further apart?
&lt;/h2&gt;&lt;p&gt;In the ecosystem, on the surface, everything looks like &amp;ldquo;open source + extensible,&amp;rdquo; but in reality, they are quite clearly branched out.&lt;/p&gt;
&lt;p&gt;OpenClaw&amp;rsquo;s ecosystem is broader and leans more towards the external access layer. Official plugins can expand channels, model providers, tools, skills, speech, web fetch, web search, memory, while community plugins and ClawHub are also growing around &amp;ldquo;connecting more platforms, connecting more accounts, connecting more workflows.&amp;rdquo; It even explicitly supports bundle formats like &lt;code&gt;.codex-plugin&lt;/code&gt;, &lt;code&gt;.claude-plugin&lt;/code&gt;, and &lt;code&gt;.cursor-plugin&lt;/code&gt;. This move is very smart; essentially, it means absorbing the shells of other ecosystems first, thereby enlarging its entry points.&lt;/p&gt;
&lt;p&gt;Looking at the OpenClaw documentation, you&amp;rsquo;ll find that it writes very extensively about sub-agents, delegates, organizational agents, thread binding, DM pairing, and group mentions. This indicates its ecosystem goal is not just for single-machine tinkering, but rather to become a truly long-running agent operating system.&lt;/p&gt;
&lt;p&gt;Hermes&amp;rsquo;s ecosystem is more like &amp;ldquo;externalizing core capabilities.&amp;rdquo; It has a Python plugin system, a memory provider plugin, a context engine plugin, and MCP server integration, as well as open skill standards like Skills Hub and &lt;code&gt;agentskills.io&lt;/code&gt;. Coupled with training-related interfaces such as batch runner, trajectory export, and Atropos, Hermes&amp;rsquo;s ecosystem naturally attracts two types of people.&lt;/p&gt;
&lt;p&gt;One type is those who use it as a personal agent.&lt;/p&gt;
&lt;p&gt;Another category is those who use it as a research, training, experimental, or workflow substrate.&lt;/p&gt;
&lt;p&gt;The needs of these two types of users are very different, but the Hermes architecture can cover both. So you might feel that while Hermes&amp;rsquo; ecosystem may not spread as widely in terms of &amp;ldquo;number of channels&amp;rdquo; and &amp;ldquo;message presence&amp;rdquo; as OpenClaw, I am more inclined to feel that its potential in the agent substrate area will be greater.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a pretty interesting detail. The official Hermes README doesn&amp;rsquo;t just have &lt;code&gt;hermes claw migrate&lt;/code&gt;; it also features a community project, &lt;code&gt;HermesClaw&lt;/code&gt;, which is used to run both the Hermes Agent and OpenClaw on the same WeChat account simultaneously. This signal is quite clear: Hermes isn&amp;rsquo;t pretending that it has no connection with OpenClaw; it is acknowledging the existence of this user migration path and then productizing this path as well.&lt;/p&gt;
&lt;h2 id=&#34;my-final-judgment&#34;&gt;My Final Judgment
&lt;/h2&gt;&lt;p&gt;If what you are looking for is a &amp;ldquo;personal assistant system that truly lives on the messaging front,&amp;rdquo; one that requires accounts, channels, routing, delegation, organizational boundaries, long-term online presence, and strong control planes, then OpenClaw&amp;rsquo;s path is more complete and feels more like a mature communication foundation.&lt;/p&gt;
&lt;p&gt;If you are looking for an &amp;ldquo;agent core that becomes more like your own workbench the more you use it,&amp;rdquo; and what you value is accumulated skills, memory composition, MCP, plugins, sub-agents, training data, and subsequent moldability, then Hermes is more convenient.&lt;/p&gt;
&lt;p&gt;So, stop simply asking &amp;ldquo;Can Hermes replace OpenClaw?&amp;rdquo;. That question is a bit rough.&lt;/p&gt;
&lt;p&gt;A more accurate way to ask is:&lt;/p&gt;
&lt;p&gt;Do you want an AI assistant that lives within a messaging network, or an AI platform that lives within local workflows and agent runtimes?&lt;/p&gt;
&lt;p&gt;If you think this through, the choice won&amp;rsquo;t be so agonizing.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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 Official Website 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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Prompts: The differences between Hermes and OpenClaw, architectural differences, differences in design philosophy, and ecosystem differences.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-idea-summary&#34;&gt;Writing Idea Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Remove the common question &amp;ldquo;Alternative Comparison&amp;rdquo; and directly set the main thread to &amp;ldquo;What is the core focus of the two projects?&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;For the architecture section, prioritize looking at the main entry points and core components in the official documentation, rather than secondary community summaries.&lt;/li&gt;
&lt;li&gt;In the design philosophy section, avoid vague value judgments and focus directly on specific designs such as Gateway, Agent Core, memory, skill, and delegate.&lt;/li&gt;
&lt;li&gt;For the ecosystem section, focus on comparing extension directions, not star ratings, popularity, or community hype wars.&lt;/li&gt;
&lt;li&gt;Fact verification must be based on official repositories and documentation visible as of 2026-04-15.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>AI writes demos quickly, and revisions are also really fast.</title>
        <link>https://ttf248.life/en/p/ai-demo-fast-rework-faster/</link>
        <pubDate>Fri, 10 Apr 2026 15:39:51 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/ai-demo-fast-rework-faster/</guid>
        <description>&lt;p&gt;Recently, I started a small C++ project using AI. The most frustrating moment isn&amp;rsquo;t when it can&amp;rsquo;t write the code, but when it spits out a seemingly decent directory structure in three minutes and casually throws in a few third-party libraries, making the demo actually runnable. That&amp;rsquo;s the problem. You haven&amp;rsquo;t even figured out what the newly introduced library supports, how the compilation link works, or where its boundaries are, so rework is basically inevitable later on.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m increasingly feeling that what AI programming fears most is not the model being dumb, but starting too greedily. Especially with languages like C++, which don&amp;rsquo;t have much scaffolding to fall back on; if you miss one step upfront, later you have to account for several steps regarding compilation, linking, library versions, and directory structure.&lt;/p&gt;
&lt;p&gt;My own conclusion on this is quite direct: AI is better suited as a pair programmer that can accelerate progress, rather than one that takes over all the initial design and dependency decisions for you. GitHub&amp;rsquo;s documentation suggests starting with simpler tasks, letting the agent master areas with clearer boundaries like bugs, documentation, tests, or technical debt; Anthropic&amp;rsquo;s Plan Mode documentation feels more like insurance for complex changes—first read the code, then create a plan, and only then decide whether to implement the change. The two approaches aren&amp;rsquo;t exactly the same, but they point in the same direction: don&amp;rsquo;t throw the hardest, messiest, most dependency-laden chunk at the AI right away.&lt;/p&gt;
&lt;p&gt;So, this article won&amp;rsquo;t talk about a bunch of specifications; instead, it will provide a small case study that you can practice with immediately. You can follow along using any empty C++ repository.&lt;/p&gt;
&lt;h2 id=&#34;its-not-that-ai-is-impossible-its-that-the-starting-point-was-too-greedy&#34;&gt;It&amp;rsquo;s not that AI is impossible, it&amp;rsquo;s that the starting point was too greedy
&lt;/h2&gt;&lt;p&gt;Writing AI projects, especially in C++, is often a very rudimentary approach.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start with a minimal working version from &lt;code&gt;main.cpp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;First, get the compilation pipeline running&lt;/li&gt;
&lt;li&gt;Then decide which libraries to include&lt;/li&gt;
&lt;li&gt;Ensure it still compiles at every step&lt;/li&gt;
&lt;li&gt;Wait until the business outline is clear before starting refactoring&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This method looks slow, but it&amp;rsquo;s actually very stable. Because at every step you know exactly what you gained, and you also know which step the problem originated from.&lt;/p&gt;
&lt;p&gt;AI is prone to disrupting this rhythm. It really likes to &amp;ldquo;complete it for you&amp;rdquo; in one go:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;ve also set up the configuration files for you.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve also extracted the logging module for you.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve also organized the error codes, utility classes, and directory structure together.&lt;/li&gt;
&lt;li&gt;It even selected the integration method for third-party libraries for you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result is that the demo looks complete, but your mental model is empty. If there&amp;rsquo;s any discrepancy between a library&amp;rsquo;s capability boundary and what you expect later on, rework won&amp;rsquo;t be in one spot, but a series of them.&lt;/p&gt;
&lt;h2 id=&#34;start-with-a-small-project-to-get-some-practice&#34;&gt;Start with a small project to get some practice
&lt;/h2&gt;&lt;p&gt;I think it would be good for practice to build a very small log scanning tool. You can name it anything, like &lt;code&gt;logscan&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It does one thing: it scans log files in a directory, counts the number of &lt;code&gt;error&lt;/code&gt; and &lt;code&gt;warn&lt;/code&gt;, and then prints the result. This problem is not big, but it&amp;rsquo;s just enough for you to go through the most common parts of a small C++ project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt; and parameter entry point&lt;/li&gt;
&lt;li&gt;Output formatting&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Configuration files&lt;/li&gt;
&lt;li&gt;Business module decomposition&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key is not how advanced the questions are, but how you break them down.&lt;/p&gt;
&lt;p&gt;I will break it down into four steps, and for each step, the AI is only allowed to do the current small part.&lt;/p&gt;
&lt;h2 id=&#34;step-one-prepare-the-output-first&#34;&gt;Step One, Prepare the Output First
&lt;/h2&gt;&lt;p&gt;First, don&amp;rsquo;t touch the logs, don&amp;rsquo;t touch the configuration, and don&amp;rsquo;t think about abstraction.&lt;/p&gt;
&lt;p&gt;Do two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a minimal &lt;code&gt;main.cpp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Include &lt;code&gt;fmt&lt;/code&gt;, and print the scanned directory and statistics results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The official documentation for &lt;code&gt;fmt&lt;/code&gt; provides very clear CMake usage. It has two targets: &lt;code&gt;fmt::fmt&lt;/code&gt; and &lt;code&gt;fmt::fmt-header-only&lt;/code&gt;. The documentation also explicitly recommends the compiled version, and the reason is quite simple: it&amp;rsquo;s more friendly to build times. CMake&amp;rsquo;s &lt;code&gt;FetchContent&lt;/code&gt; documentation is also very suitable for starting new projects because it fetches dependencies during the configure stage, rather than downloading them temporarily during the build stage.&lt;/p&gt;
&lt;p&gt;In this step, I will directly fix the dependency strategy and not let the AI play freely.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Either unify &lt;code&gt;FetchContent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Or unify &lt;code&gt;find_package&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t use vcpkg one moment, &lt;code&gt;add_subdirectory&lt;/code&gt; the next, and manually copy headers after that&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If it&amp;rsquo;s a practice project, I usually constrain the AI like this:&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;Do not optimize the architecture all at once.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;The current goal is only this step, and the project must always be compilable.
&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;This step involves only 3 things:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1. Include fmt in the root CMakeLists.txt.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2. Use fmt in main.cpp to print the passed directory and scanning results.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3. Keep the existing target names and directory structure; do not add new logging, configuration, or testing frameworks.
&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;First, give me a modification checklist, then provide the code.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;After completion, provide the compilation command and expected output.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s a detail here that is quite important. If AI is not constrained, it can easily treat &amp;ldquo;doing something extra in the back&amp;rdquo; as a bonus point. You must explicitly tell it that it cannot go beyond the scope this round.&lt;/p&gt;
&lt;h2 id=&#34;step-two-upload-logs&#34;&gt;Step Two, Upload Logs
&lt;/h2&gt;&lt;p&gt;Once the first step is done, it can be compiled, run, and the output looks clean, then add &lt;code&gt;spdlog&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The official &lt;code&gt;spdlog&lt;/code&gt; README is actually very straightforward about what it can do. It can handle console logging, as well as regular files, rolling files, daily rotation, and even a backtrace ring buffer. The problem is this: with so many capabilities, AI gets easily excited and wants to cram everything into the first version—colored console output, rolling logs, daily archiving, global logger factory, all at once.&lt;/p&gt;
&lt;p&gt;No need.&lt;/p&gt;
&lt;p&gt;For this step, I will only have it do console logging, or at most add the simplest file logging. Because what you really need to learn at this point is not &amp;ldquo;how to design a complete logging system,&amp;rdquo; but three things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where to initialize the logger&lt;/li&gt;
&lt;li&gt;How to get the logger in business logic&lt;/li&gt;
&lt;li&gt;How to separate logging and normal output when an error occurs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you immediately implement &lt;code&gt;rotating_file_sink&lt;/code&gt; or &lt;code&gt;daily_file_sink&lt;/code&gt;, while you quickly gain a &amp;ldquo;feature-rich&amp;rdquo; logging module, you might not actually know if you need rotation or daily splitting, or if printing to the terminal during debugging is sufficient.&lt;/p&gt;
&lt;p&gt;To put it plainly, understanding how to use &lt;code&gt;spdlog::info()&lt;/code&gt; is more important than making the logging factory look pretty.&lt;/p&gt;
&lt;h2 id=&#34;step-three-touching-the-configuration-file&#34;&gt;Step Three: Touching the Configuration File
&lt;/h2&gt;&lt;p&gt;The configuration file section is easier for AI to turn into a large project.&lt;/p&gt;
&lt;p&gt;I prefer to practice with a simpler library like &lt;code&gt;toml++&lt;/code&gt;, not because it&amp;rsquo;s the &amp;ldquo;most powerful,&amp;rdquo; but because it&amp;rsquo;s very suitable for the initial stages of a new project. It is itself a C++17 header-only TOML parser, and the examples in the README are also quite short; you can directly read with &lt;code&gt;toml::parse_file(&amp;quot;configuration.toml&amp;quot;)&lt;/code&gt;. More importantly, its single-header mode is incredibly convenient. The official description is quite interesting: the single-header approach is just &amp;ldquo;throwing &lt;code&gt;toml.hpp&lt;/code&gt; into your source tree,&amp;rdquo; and then there&amp;rsquo;s &amp;ldquo;no second step.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;For this step, do not use a &amp;ldquo;Configuration Center,&amp;rdquo; but read three fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scan Directory&lt;/li&gt;
&lt;li&gt;Keyword List&lt;/li&gt;
&lt;li&gt;Write Output to File&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then put them into a very thin &lt;code&gt;AppConfig&lt;/code&gt; struct.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s enough.&lt;/p&gt;
&lt;p&gt;A lot of rework, it actually comes down to this. You haven&amp;rsquo;t even figured out if the configuration should be read once at startup or hot-updated at runtime; whether it&amp;rsquo;s only for the local CLI or if you plan to reuse it for service processes later. AI has already taken five or six layers ahead of you. If you change direction later, all that &amp;ldquo;complete design&amp;rdquo; from before will just become a burden.&lt;/p&gt;
&lt;h2 id=&#34;step-four-decomposing-business-modules&#34;&gt;Step Four: Decomposing Business Modules
&lt;/h2&gt;&lt;p&gt;For libraries like &lt;code&gt;fmt&lt;/code&gt;, &lt;code&gt;spdlog&lt;/code&gt;, and &lt;code&gt;toml++&lt;/code&gt;, please read through the official documentation yourself, run them successfully in practice, and &lt;em&gt;then&lt;/em&gt; ask AI to help you break down the modules.&lt;/p&gt;
&lt;p&gt;When you break it down like this, then you&amp;rsquo;ll have a clear understanding.&lt;/p&gt;
&lt;p&gt;I usually receive the following types of files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main.cpp&lt;/code&gt; is only responsible for assembly&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app_config.{h,cpp}&lt;/code&gt; is only responsible for configuration reading&lt;/li&gt;
&lt;li&gt;&lt;code&gt;logger.{h,cpp}&lt;/code&gt; is only responsible for log initialization&lt;/li&gt;
&lt;li&gt;&lt;code&gt;log_scanner.{h,cpp}&lt;/code&gt; handles business logic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that the splitting happens at this step, not from the first step. In the first step, just lining up the table of contents neatly is often only visually pleasing and doesn&amp;rsquo;t necessarily mean it&amp;rsquo;s cognitively clearer.&lt;/p&gt;
&lt;h2 id=&#34;whats-truly-useful-isnt-some-complex-specification&#34;&gt;What&amp;rsquo;s truly useful isn&amp;rsquo;t some complex specification
&lt;/h2&gt;&lt;p&gt;Many online AI coding guidelines are very complicated, with dozens of rules. It&amp;rsquo;s tiring just looking at them. The ones I kept for myself are actually only three, and they are enough.&lt;/p&gt;
&lt;p&gt;First, create a &amp;ldquo;resource card.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Every time I prepare to reference a third-party library, I first ask the AI to answer these four questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What problem does this library solve?&lt;/li&gt;
&lt;li&gt;How far has the official support reached?&lt;/li&gt;
&lt;li&gt;Which 1 or 2 capabilities am I planning to use this time?&lt;/li&gt;
&lt;li&gt;What new constraints will it bring to building, deploying, and running?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you can&amp;rsquo;t explain this card, don&amp;rsquo;t play it yet.&lt;/p&gt;
&lt;p&gt;Second, only let the AI pass one checkpoint at a time.&lt;/p&gt;
&lt;p&gt;For example, in this round, it is only allowed to complete:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Can code&lt;/li&gt;
&lt;li&gt;Can run&lt;/li&gt;
&lt;li&gt;Can see expected output&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don&amp;rsquo;t add things like &amp;ldquo;while I&amp;rsquo;m at it, let&amp;rsquo;s refactor the directory,&amp;rdquo; &amp;ldquo;I&amp;rsquo;ll quickly add a configuration class,&amp;rdquo; or &amp;ldquo;let&amp;rsquo;s unify the exception handling system&amp;rdquo; in the same round. In C++ projects, the more &amp;ldquo;conveniently&amp;rdquo; you do something, the easier it is to muddy the source of the problem.&lt;/p&gt;
&lt;p&gt;Article Three, a rollback point must be left after every round.&lt;/p&gt;
&lt;p&gt;Even if you don&amp;rsquo;t commit, you should be able to answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What exactly changed in this step?&lt;/li&gt;
&lt;li&gt;If it&amp;rsquo;s wrong, which parts need to be deleted?&lt;/li&gt;
&lt;li&gt;After reverting to the previous step, can the project still be compiled?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will find that this line of thinking is actually very similar to manual development. The difference is that before you wrote the code yourself, now you are supervising the AI so it doesn&amp;rsquo;t run wild.&lt;/p&gt;
&lt;h2 id=&#34;a-more-practical-use-case-for-ai&#34;&gt;A More Practical Use Case for AI
&lt;/h2&gt;&lt;p&gt;If you really want to master this set of things, I suggest doing three consecutive rounds.&lt;/p&gt;
&lt;p&gt;In the first round, only let the AI help you with &lt;code&gt;fmt&lt;/code&gt;, and read through the CMake configuration and output code yourself.&lt;/p&gt;
&lt;p&gt;Round two, only let the AI handle &lt;code&gt;spdlog&lt;/code&gt;. You decide whether it should be console logging or file logging; you cannot use both together.&lt;/p&gt;
&lt;p&gt;Round three, only let the AI handle &lt;code&gt;toml++&lt;/code&gt;, and you decide the configuration items yourself; do not allow the AI to guess future requirements for you.&lt;/p&gt;
&lt;p&gt;After going through these three rounds, your feeling about AI programming will change a lot. Before, it was &amp;ldquo;how did it generate so much for me all at once,&amp;rdquo; but later it will slowly become, &amp;ldquo;how small should I prompt the next step to be most worthwhile.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I think this is the most valuable skill in AI programming right now.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not about creating a demo.&lt;/p&gt;
&lt;p&gt;but rather to steadily move a project forward.&lt;/p&gt;
&lt;p&gt;To be honest, this set of methods isn&amp;rsquo;t very novel; it&amp;rsquo;s even a bit old-fashioned. But old methods are often resistant to rework. Especially with a language like C++, if you let the AI do less and confirm more yourself at the beginning, things will genuinely become much easier later on. Otherwise, you might fly up in three minutes only to have to go back to the drawing board for three hours—and the one who looks foolish is still yourself.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Prompt: Writing a project manually, especially for languages like C++ that don&amp;rsquo;t have scaffolds. First, create a basic framework starting from &lt;code&gt;main&lt;/code&gt; and gradually expand the logic by adding modules, such as configuration files, logging modules, and iterative business modules. My habit is to ensure it compiles at every step and refactor incrementally; what I write initially might not be the optimal solution. Regarding AI programming, often nowadays, steps are skipped, and too little thought is put into the early stages, especially when introducing new third-party libraries. While AI can quickly generate a demo, because I don&amp;rsquo;t fully understand the details of newly introduced third-party libraries or what functions they support, the rework rate for related logic is significantly higher than manual development. Do you have any good practical examples that I can learn from? I know there are many complex AI programming guidelines online, but I want something simpler and quickly implementable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-idea-summary&#34;&gt;Writing Idea Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Narrow the topic down to the &lt;code&gt;tooling&lt;/code&gt; lane, focusing on where things become easier or conversely more awkward after AI changes workflows.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t write a general checklist of best practices; instead, turn it into an incremental hands-on case study using a small C++ project.&lt;/li&gt;
&lt;li&gt;Keep the core judgment that the user &amp;ldquo;starts from &lt;code&gt;main&lt;/code&gt;, can compile at every step, and refactors gradually,&amp;rdquo; and make this the main narrative thread throughout the article.&lt;/li&gt;
&lt;li&gt;Supplemented with primary sources for Anthropic, GitHub, CMake, fmt, spdlog, and toml++ to avoid guessing about tool capabilities and dependency methods.&lt;/li&gt;
&lt;li&gt;The article structure progresses according to &lt;code&gt;fmt -&amp;gt; spdlog -&amp;gt; toml++ -&amp;gt; module decomposition&lt;/code&gt;, intentionally preventing AI from achieving everything in one go.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>VS Code for C&#43;&#43;, don&#39;t forget CMake and GDB Printer</title>
        <link>https://ttf248.life/en/p/vscode-cpp-debug-prelaunch-cmake-gdb-printer/</link>
        <pubDate>Thu, 09 Apr 2026 19:21:36 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/vscode-cpp-debug-prelaunch-cmake-gdb-printer/</guid>
        <description>&lt;p&gt;Previously, when I debugged C++ in VS Code, the configuration basically stopped at &lt;code&gt;launch.json&lt;/code&gt;, maybe with an extra line for GDB.
Fill in the &lt;code&gt;program&lt;/code&gt;, fill in the &lt;code&gt;gdb&lt;/code&gt;, set the breakpoints. And then what? Then every time before debugging, I had to manually run &lt;code&gt;cmake --build&lt;/code&gt; in the terminal.
What was even more annoying was that after setting breakpoints on custom prices, contracts, or order types, the VS Code debug window often only showed a bunch of internal fields. The data was correct, but it wasn&amp;rsquo;t human-readable.
The ridiculous part is that some tutorials I saw before stopped around &lt;code&gt;launch.json&lt;/code&gt;. It wasn&amp;rsquo;t until recently, when I had an AI set up a new project for me, that it conveniently added &lt;code&gt;preLaunchTask&lt;/code&gt; and &lt;code&gt;gdb_printers.py&lt;/code&gt;, that I realized: debugging C++ in VS Code isn&amp;rsquo;t just about starting GDB. You can automatically trigger CMake compilation before debugging, and after hitting a breakpoint, you can even let GDB load a Python script to format business types into something readable for us.
Honestly, this isn&amp;rsquo;t some black magic.
But it perfectly filled two annoying gaps in daily C++ debugging: &lt;strong&gt;pre-launch build&lt;/strong&gt; and &lt;strong&gt;variable display after a breakpoint&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;only-half-was-configured-before&#34;&gt;Only Half Was Configured Before
&lt;/h2&gt;&lt;p&gt;The &lt;code&gt;.vscode/launch.json&lt;/code&gt; in many tutorials is probably something like this:&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;This part cannot be wrong.
The official C++ example in VS Code also uses a similar structure: &lt;code&gt;type&lt;/code&gt; uses &lt;code&gt;cppdbg&lt;/code&gt;, &lt;code&gt;MIMode&lt;/code&gt; uses &lt;code&gt;gdb&lt;/code&gt;, and pretty-printing is enabled in &lt;code&gt;setupCommands&lt;/code&gt;.
But one crucial step is missing.
When you press F5, it launches the executable file pointed to by &lt;code&gt;program&lt;/code&gt;. The problem is, was this file just compiled?
If not, then you are debugging the old program from last time.
I used to do this stupid thing often. I changed the code and set breakpoints, debugged for a while, only to realize later: Oh, I forgot to recompile.&lt;/p&gt;
&lt;h2 id=&#34;prelaunchtask-is-the-hook&#34;&gt;preLaunchTask is the hook
&lt;/h2&gt;&lt;p&gt;VS Code Tasks are essentially about hooking external commands into the editor.
Compiling, testing, and packaging—what used to be typed in the terminal can now be written into &lt;code&gt;.vscode/tasks.json&lt;/code&gt;. Then, &lt;code&gt;preLaunchTask&lt;/code&gt; in &lt;code&gt;launch.json&lt;/code&gt; finds that task by its label.
For a minimal CMake project, it could look like this:&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;Here is the &lt;code&gt;CMakeLists.txt&lt;/code&gt; first:&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;And here is &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;Notice the &lt;code&gt;cmake --build&lt;/code&gt; here.
The official CMake build entry point is this format: &lt;code&gt;cmake --build &amp;lt;dir&amp;gt;&lt;/code&gt;. It calls the underlying native build tool, such as Make, Ninja, or MSBuild.
This means VS Code doesn&amp;rsquo;t need to know whether your project should use &lt;code&gt;make&lt;/code&gt; or &lt;code&gt;ninja&lt;/code&gt;.
CMake handles that part.&lt;/p&gt;
&lt;h2 id=&#34;before-f5-first-compile&#34;&gt;Before F5, first compile
&lt;/h2&gt;&lt;p&gt;Then go back to &lt;code&gt;.vscode/launch.json&lt;/code&gt; and add &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;This line is the key:&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;The flow when pressing F5 becomes:&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 starts the latest program
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you are on Windows + MinGW, the &lt;code&gt;program&lt;/code&gt; probably needs to be changed to a path with &lt;code&gt;.exe&lt;/code&gt;, and &lt;code&gt;miDebuggerPath&lt;/code&gt; should also point to your own &lt;code&gt;gdb.exe&lt;/code&gt;.
If it&amp;rsquo;s a multi-configuration generator like Visual Studio Generator, you usually need to add &lt;code&gt;--config Debug&lt;/code&gt; to the build arguments.
But the main flow remains the same.
Before debugging, let VS Code run the build task first; don&amp;rsquo;t rely on memory alone.&lt;/p&gt;
&lt;h2 id=&#34;another-pain-point-incomprehensible-custom-types&#34;&gt;Another Pain Point: Incomprehensible Custom Types
&lt;/h2&gt;&lt;p&gt;Just connecting the compilation link is not enough.
If you wrap a business type slightly in your C++ code, the variable window on the left side of VS Code starts looking ugly.
Take this example.
&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;// Set a breakpoint here to observe watchlist and 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;In the business logic, &lt;code&gt;market::Price&lt;/code&gt; represents a price.
But what the debugger might default to showing is only:&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;Yes, the data is correct.
But in my head, I still have to manually convert &lt;code&gt;36005000&lt;/code&gt; back to &lt;code&gt;3600.5000&lt;/code&gt;. If a project has a bunch of &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;, the debug window quickly turns into a structure graveyard.
This is when you need GDB pretty-printers.
The official GDB manual states it very clearly: It provides a mechanism that can use Python code to pretty-print values, and this mechanism works for both MI and command line.
VS Code&amp;rsquo;s C++ debugging uses exactly this GDB/MI path.&lt;/p&gt;
&lt;h2 id=&#34;writing-a-python-presentation-layer-for-business-types&#34;&gt;Writing a Python Presentation Layer for Business Types
&lt;/h2&gt;&lt;p&gt;Create &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;Then source it in &lt;code&gt;launch.json&lt;/code&gt;&amp;rsquo;s &lt;code&gt;setupCommands&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;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;Press F5 again.
When you look at the debug window this time, the display for &lt;code&gt;Price&lt;/code&gt; should be close to:&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;And &lt;code&gt;Instrument&lt;/code&gt; will change from a blob of arrays and structs to something closer to the business logic:&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;I deliberately say &amp;ldquo;close to&amp;rdquo; here because the pretty-printer needs to match the compiler, type names, and actual field layout.
If your business types contain nested &lt;code&gt;std::vector&lt;/code&gt;, &lt;code&gt;std::array&lt;/code&gt;, or smart pointers, you might need to write more code in Python to access those fields. Don&amp;rsquo;t expect one script to cover everything.
But the idea is solid.
Separate what &amp;ldquo;you want to see when debugging&amp;rdquo; into its own layer of Python presentation logic.&lt;/p&gt;
&lt;h2 id=&#34;dont-treat-launchjson-as-everything&#34;&gt;Don&amp;rsquo;t treat launch.json as everything
&lt;/h2&gt;&lt;p&gt;Looking at this set of configurations now, I think there are at least three layers.
&lt;code&gt;.vscode/launch.json&lt;/code&gt; is responsible for &amp;ldquo;how to start the debugger.&amp;rdquo;
&lt;code&gt;.vscode/tasks.json&lt;/code&gt; is responsible for &amp;ldquo;building the project first before starting the debugger.&amp;rdquo;
&lt;code&gt;tools/gdb_printers.py&lt;/code&gt; is responsible for &amp;ldquo;displaying variables in a human-readable way after a breakpoint.&amp;rdquo;
Before, I only configured the first layer.
It worked, but it was very rudimentary.
Smooth C++ debugging shouldn&amp;rsquo;t be like: &amp;ldquo;I have to compile it in the terminal first, then come back and press F5, and then manually calculate the meaning of fields in the variables window.&amp;rdquo;
The more comfortable workflow should be: Press F5, VS Code automatically runs CMake, GDB automatically loads the project&amp;rsquo;s Python printer, and when a breakpoint is hit, the debug window directly speaks business language.
What was most valuable about AI helping me set up this project wasn&amp;rsquo;t how much C++ code it generated.
It was that it bridged these old tools together.
It&amp;rsquo;s not that I didn&amp;rsquo;t know CMake before, or that I didn&amp;rsquo;t know GDB.
I just missed configuring those intermediate hooks.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Prompt: $blog-writer vscode for C++ development, previous tutorials only showed simple configuration of gdb debugging information in launch.json, and didn&amp;rsquo;t mention configuring preLaunchTask. Being able to automatically trigger cmake compilation is a great feature when setting up new projects with AI; if the code has many custom data types that cannot be directly displayed in the VS Code debug window, one can introduce a Python script for preprocessing so that they can all be displayed. Regarding the above content, actual code examples are needed for everything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-idea-summary&#34;&gt;Writing Idea Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;The main thread should focus on the idea that &amp;ldquo;previously, only &lt;code&gt;launch.json&lt;/code&gt; was configured, but it missed the two layers of glue: pre-build and runtime display during debugging.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Keep the trigger point provided by the user: Discovering that when setting up a new AI project, &lt;code&gt;preLaunchTask&lt;/code&gt; can automatically trigger CMake compilation.&lt;/li&gt;
&lt;li&gt;Organize code examples around a minimal CMake project, including &lt;code&gt;CMakeLists.txt&lt;/code&gt;, &lt;code&gt;tasks.json&lt;/code&gt;, &lt;code&gt;launch.json&lt;/code&gt;, custom C++ types, and GDB Python pretty-printer.&lt;/li&gt;
&lt;li&gt;For factual verification, prioritize official documentation from VS Code, CMake, and GDB; the main text should not translate entire documents but only retain facts relevant to this debugging pipeline.&lt;/li&gt;
&lt;li&gt;Provide a warning regarding the boundaries of the pretty-printer: Type names, standard library implementations, and field layouts can affect the Python script, so users must adapt it for their specific types in the project.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>What is this process of layering folders and then wrapping them in a namespace called?</title>
        <link>https://ttf248.life/en/p/cpp-folder-namespace-what-is-it-called/</link>
        <pubDate>Thu, 09 Apr 2026 02:31:07 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/cpp-folder-namespace-what-is-it-called/</guid>
        <description>&lt;p&gt;When I recently wrote the algorithm service, as soon as I implemented modules like &lt;code&gt;twap&lt;/code&gt; and &lt;code&gt;vwap&lt;/code&gt;, this old problem popped up again.&lt;/p&gt;
&lt;p&gt;If we rely on class names to enforce semantics in C++, the naming convention quickly becomes out of control. Things like &lt;code&gt;TwapOrderManager&lt;/code&gt;, &lt;code&gt;VwapOrderManager&lt;/code&gt;, and &lt;code&gt;AlgoOrderManager&lt;/code&gt; sound like, &amp;ldquo;I know my structure isn&amp;rsquo;t contained, but I&amp;rsquo;ll at least add a prefix.&amp;rdquo; Frankly speaking, organizing by folders and then adding a layer of &lt;code&gt;namespace&lt;/code&gt; isn&amp;rsquo;t about code snobbery; it&amp;rsquo;s filling the gap that C++ has because it lacks Java&amp;rsquo;s native &lt;code&gt;package&lt;/code&gt; system.&lt;/p&gt;
&lt;h2 id=&#34;to-state-the-conclusion-first&#34;&gt;To state the conclusion first
&lt;/h2&gt;&lt;p&gt;If I have to find a software engineering term that is the closest, I think the two words that should be mentioned are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Modularization&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Namespace management&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If I want to be more precise, this type of directory organization is often doing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Packaging by feature / Packaging by domain&lt;/strong&gt;, which is commonly referred to as &lt;code&gt;package by feature&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If the business boundaries are already very strong, it will also carry a bit of &lt;strong&gt;bounded context&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words, it is not a small concept with a single standard translation name; rather, it is more like several design principles stacked together.&lt;/p&gt;
&lt;h2 id=&#34;what-is-actually-solved-here-is-not-just-about-looking-good&#34;&gt;What is actually solved here is not just about &amp;ldquo;looking good&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;When many people first see a &lt;code&gt;namespace&lt;/code&gt;, their immediate thought is to avoid name collisions. This is certainly true; the &lt;code&gt;namespace&lt;/code&gt; in C++ standards was originally designed to prevent naming conflicts in large projects &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;. However, when it comes to actual business code, its value goes far beyond that.&lt;/p&gt;
&lt;p&gt;Once the directory structure and &lt;code&gt;namespace&lt;/code&gt; are aligned, class names no longer need to repeat the higher-level semantics.&lt;/p&gt;
&lt;p&gt;For example, previously one might write something like this:&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;After adopting directories and namespaces, a more natural way to write it is usually:&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;This removes the redundant prefix, making the semantics clearer. Because &amp;ldquo;which context it belongs to&amp;rdquo; is no longer stuffed into the class name, but rather expressed by the directory and &lt;code&gt;namespace&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Therefore, this approach essentially involves &lt;strong&gt;extracting contextual information from the names and letting the structure carry it&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;this-is-more-like-packaging-by-feature&#34;&gt;This is more like &amp;ldquo;packaging by feature&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;If your directory looks like this:&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;Then you are no longer doing traditional &amp;ldquo;packaging by technical layer.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The traditional way is more like this:&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;This structure looks neat at the beginning, but as the business logic grows, the &lt;code&gt;service&lt;/code&gt; directory will look like a junk heap. Classes related to one feature get scattered across different directories, making it hard to read the code because your mind keeps jumping around.&lt;/p&gt;
&lt;p&gt;Whereas the &lt;code&gt;twap&lt;/code&gt; or &lt;code&gt;vwap&lt;/code&gt; approach is closer to &lt;code&gt;package by feature&lt;/code&gt;. This means a directory first answers the question, &amp;ldquo;What is this business module about?&amp;rdquo;, and then places all the objects and processes needed for that specific business module inside it. This concept is more common in the Java world, but it works just as well in C++; the difference is that what carries it isn&amp;rsquo;t a native &lt;code&gt;package&lt;/code&gt;, but rather a combination of &amp;ldquo;directory + &lt;code&gt;namespace&lt;/code&gt; + header file boundaries.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Simply put, you are &lt;strong&gt;organizing code by the reason for change, not by the appearance of the code.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;taking-it-one-step-further-its-about-high-cohesion-and-low-coupling&#34;&gt;Taking it one step further, it&amp;rsquo;s about high cohesion and low coupling
&lt;/h2&gt;&lt;p&gt;The frequently mentioned phrase in software engineering, &amp;ldquo;high cohesion, low coupling,&amp;rdquo; actually falls into this area.&lt;/p&gt;
&lt;p&gt;Since the objects under &lt;code&gt;twap&lt;/code&gt; collaborate frequently with each other, they should be placed closer together, and their namespaces should also be close; &lt;code&gt;vwap&lt;/code&gt; is similar to it but not quite the same, so it deserves its own boundary. Doing this has several direct benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Class names within the same module can be shorter&lt;/li&gt;
&lt;li&gt;Dependencies between modules are easier to see&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s easier to judge during refactoring whether changes might propagate to other areas&lt;/li&gt;
&lt;li&gt;If you eventually need to split a certain capability into an independent library, the cost will be smaller.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is also why many mature projects end up looking like they have consistent &amp;ldquo;directory boundaries + namespace boundaries + compilation boundaries.&amp;rdquo; In projects like QuickFIX, you can see a similar idea: types and extension points are kept under clear namespaces rather than being artificially separated by very long class prefixes &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;when-its-not-enough-anymore&#34;&gt;When It&amp;rsquo;s Not Enough Anymore
&lt;/h2&gt;&lt;p&gt;However, don&amp;rsquo;t mythologize this either.&lt;/p&gt;
&lt;p&gt;Simply layering folders and wrapping them in a &lt;code&gt;namespace&lt;/code&gt; can only indicate that you are moving towards modularization; it doesn&amp;rsquo;t mean the architecture is automatically sound. Several common pitfalls are also obvious:&lt;/p&gt;
&lt;h3 id=&#34;directory-and-namespace-mismatch&#34;&gt;Directory and Namespace Mismatch
&lt;/h3&gt;&lt;p&gt;The directory is &lt;code&gt;twap/&lt;/code&gt;, but the code contains scattered global classes, or &lt;code&gt;namespace common&lt;/code&gt; floating everywhere. This is basically useless.&lt;/p&gt;
&lt;h3 id=&#34;module-boundaries-are-false-boundaries&#34;&gt;Module Boundaries are False Boundaries
&lt;/h3&gt;&lt;p&gt;There are &lt;code&gt;twap&lt;/code&gt; and &lt;code&gt;vwap&lt;/code&gt; on the surface, but they actually &lt;code&gt;#include&lt;/code&gt; each other. The common logic can leak through freely, and in the end, it just moves the files around without reducing any coupling.&lt;/p&gt;
&lt;h3 id=&#34;the-longer-the-common-directory-the-fatter-it-gets&#34;&gt;The longer the &lt;code&gt;common&lt;/code&gt; directory, the fatter it gets
&lt;/h3&gt;&lt;p&gt;This is the most common issue. Many projects are structured quite well at first, but later find it troublesome and start dumping things into &lt;code&gt;common&lt;/code&gt;, &lt;code&gt;base&lt;/code&gt;, or &lt;code&gt;util&lt;/code&gt;. By the end, instead of having truly stable abstractions that have been properly developed, they end up with a massive pitfall that everyone can touch and everyone depends on.&lt;/p&gt;
&lt;h3 id=&#34;structure-by-layer-only-not-by-business-domain&#34;&gt;Structure by Layer Only, Not by Business Domain
&lt;/h3&gt;&lt;p&gt;If your directory structure is always &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;, then many business concepts actually have no home. Class names are forced to get longer and longer, cramming everything that should be expressed by the structure back into the name itself.&lt;/p&gt;
&lt;h2 id=&#34;so-what-should-it-be-called&#34;&gt;So, what should it be called?
&lt;/h2&gt;&lt;p&gt;If I were communicating this within a team, I think there are three levels of description:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you just want to speak plain language: &lt;strong&gt;Modularization based on directories and namespaces&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;If you want to sound more engineering-focused: &lt;strong&gt;Package organization by feature, combined with hierarchical namespaces&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;If these boundaries are already strongly bound to business semantics: &lt;strong&gt;Module division with a bounded context feel&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I personally lean towards the second option. Because it is closest to the scenario you described.&lt;/p&gt;
&lt;p&gt;What you&amp;rsquo;re doing isn&amp;rsquo;t about C++ syntax tricks, nor is it just about naming conventions; you are doing something more substantial: &lt;strong&gt;Using structure to carry semantics, allowing names to return to their true meaning&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When class names get shorter and file names get shorter, what you first see when reading the code is an object with clear context like &lt;code&gt;twap::OrderManager&lt;/code&gt;, rather than something that mixes directory responsibility, naming responsibility, and implementation responsibility all together like &lt;code&gt;TwapOrderManagerImpl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Only then does the code start to feel somewhat like Java after its package structure has matured.&lt;/p&gt;
&lt;h2 id=&#34;the-last-sentence&#34;&gt;The Last Sentence
&lt;/h2&gt;&lt;p&gt;So, in software engineering, there are corresponding concepts, but there isn&amp;rsquo;t one single standard answer.&lt;/p&gt;
&lt;p&gt;Broadly speaking, it&amp;rsquo;s called modularization. In terms of code organization, it&amp;rsquo;s called feature-based packaging, plus namespace management. In terms of domain boundaries, it has something to do with bounded context.&lt;/p&gt;
&lt;p&gt;How should I put it? The most valuable aspect of this practice is never that &amp;ldquo;the names look neat,&amp;rdquo; but rather that you finally don&amp;rsquo;t have to rely on super long prefixes to compensate for structural deficiencies.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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++ Official Site&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 Repository&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Prompt: When developing code in C++, a good practice is to organize it by folders and then wrap each class with a namespace, similar to Java&amp;rsquo;s package. This can effectively reduce redundant content in file names and class names. A classic project is QuickFIX, and we have recently used a similar design for algorithm services. Many functional modules like TWAP and VWAP are similar. Is there a specific concept in software engineering for this?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-approach-summary&#34;&gt;Writing Approach Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Start by focusing on the actual trigger points of &lt;code&gt;twap&lt;/code&gt; and &lt;code&gt;vwap&lt;/code&gt; in algorithm services, making the judgment clear first.&lt;/li&gt;
&lt;li&gt;Instead of forcing the problem into a single noun, it is broken down into more accurate levels such as modularization, namespace management, and packaging by feature.&lt;/li&gt;
&lt;li&gt;The correspondence between QuickFIX and Java packages has been retained to illustrate that C++ often needs directories and namespaces to supplement structural semantics.&lt;/li&gt;
&lt;li&gt;The focus is on &amp;ldquo;using structure to carry semantics,&amp;rdquo; rather than remaining at a superficial explanation like &amp;ldquo;avoiding name collisions.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Added reference links for &lt;code&gt;namespace&lt;/code&gt;, &lt;code&gt;package by feature&lt;/code&gt;, and &lt;code&gt;bounded context&lt;/code&gt; to facilitate further development.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Google has released Gemma 4 this time (III)</title>
        <link>https://ttf248.life/en/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/en/p/gemma-4-series-vram-cliff-and-mac-unified-memory/</guid>
        <description>&lt;p&gt;While browsing the forum this time, what struck me most wasn&amp;rsquo;t which company released another leaderboard, but a very basic statement: &amp;ldquo;Not enough VRAM; no matter how large the parameters are, it&amp;rsquo;s useless.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Previously, I always understood &amp;ldquo;slow model&amp;rdquo; as a computational power issue. However, the more I read, the clearer it became that often, the problem isn&amp;rsquo;t that the GPU can&amp;rsquo;t compute it, but rather that the data cannot reside in the right place. Just by changing the memory path, the token speed doesn&amp;rsquo;t just slow down; it drops drastically.&lt;/p&gt;
&lt;p&gt;The previous two posts covered the preliminary issues. &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/gemma-4-series-models-and-license/&#34; &gt;First Post&lt;/a&gt; discussed the release and protocol, and &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/gemma-4-series-local-test-on-rtx-3060/&#34; &gt;Second Post&lt;/a&gt; explained why we should first look at &lt;code&gt;26B A4B&lt;/code&gt; on the 3060 12GB. This final post will only discuss how speed actually collapses.&lt;/p&gt;
&lt;h2 id=&#34;out-of-vram-and-not-just-a-little-bit-slow&#34;&gt;Out of VRAM, and Not Just a Little Bit Slow
&lt;/h2&gt;&lt;p&gt;When you break down the process of inference, there are two particularly critical components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Model weights&lt;/li&gt;
&lt;li&gt;KV cache&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The weights define the model itself, while the KV cache records the state of previous tokens. The longer the context, the larger the KV cache. As long as both these parts can be stably kept in the GPU VRAM, generating a token basically involves reading data, performing calculations, and writing back results within high-bandwidth memory, and the speed is usually quite good.&lt;/p&gt;
&lt;p&gt;The real problem is when you run out of VRAM.
Once it doesn&amp;rsquo;t fit, the inference framework has to compromise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Storing some weights in system memory&lt;/li&gt;
&lt;li&gt;Or storing some KV cache in system memory&lt;/li&gt;
&lt;li&gt;Or even shuttling data back and forth between the CPU and GPU&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, the issue is no longer about &amp;ldquo;a little bit of extra computation,&amp;rdquo; but rather &amp;ldquo;waiting for data with every single token.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;why-the-cliff-not-a-linear-slowdown&#34;&gt;Why the Cliff, Not a Linear Slowdown
&lt;/h2&gt;&lt;p&gt;When many people first encounter this pitfall, their intuition is that if the model goes from &lt;code&gt;14B&lt;/code&gt; to &lt;code&gt;31B&lt;/code&gt;, it will just be more than twice as slow, which they can tolerate.&lt;/p&gt;
&lt;p&gt;Reality is not like that.&lt;/p&gt;
&lt;p&gt;The real dividing line isn&amp;rsquo;t doubling the parameters; it&amp;rsquo;s whether or not the working set crosses the VRAM boundary.&lt;/p&gt;
&lt;p&gt;As long as it hasn&amp;rsquo;t crossed, increasing model size usually results in a predictable slowdown.&lt;/p&gt;
&lt;p&gt;Once it crosses, the system state changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Previously, it was an &amp;ldquo;on-chip closed loop&amp;rdquo; within VRAM.&lt;/li&gt;
&lt;li&gt;Now, it becomes &amp;ldquo;VRAM + System RAM + Bus Transfer.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Changing the path completely changes the cost. This is especially true during the decoding stage, which inherently proceeds token by token with a small batch size. At this point, what is most feared is having every single token wait for a batch of data to be transferred across devices.&lt;/p&gt;
&lt;p&gt;So, you see a very typical phenomenon:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The model isn&amp;rsquo;t unable to run.&lt;/li&gt;
&lt;li&gt;The GPU isn&amp;rsquo;t completely idle either.&lt;/li&gt;
&lt;li&gt;But the tokens/s rate is extremely poor.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is what people call the &amp;ldquo;cliff.&amp;rdquo; It&amp;rsquo;s not that the model suddenly got dumber; it&amp;rsquo;s that the memory path suddenly became inefficient.&lt;/p&gt;
&lt;h2 id=&#34;why-26b-a4b-is-friendlier-to-local-players&#34;&gt;Why &lt;code&gt;26B A4B&lt;/code&gt; is Friendlier to Local Players
&lt;/h2&gt;&lt;p&gt;This also explains why I have consistently favored &lt;code&gt;26B A4B&lt;/code&gt; in my previous article.
Its total parameter count is certainly not small, but only about &lt;code&gt;3.8B&lt;/code&gt; are actually activated per token. This means that under similar deployment conditions, the pressure it puts on computation and VRAM paths is often easier to control compared to dense large models.
This isn&amp;rsquo;t magic.
If your context window gets too long, or if quantization and framework support are inadequate, it will struggle just like any other model. However, compared to a dense model that immediately maxes out the entire VRAM, &lt;code&gt;26B A4B&lt;/code&gt; feels more like a realistic path for consumer-grade GPUs.
So, often the issue isn&amp;rsquo;t whether &lt;code&gt;31B&lt;/code&gt; is weak, but rather which model is better suited for long-term coexistence with local hardware.&lt;/p&gt;
&lt;h2 id=&#34;why-macs-seem-less-prone-to-running-out-of-memory&#34;&gt;Why Macs seem &amp;ldquo;less prone to running out of memory&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;What&amp;rsquo;s most different about Mac is not the model, but the memory architecture.
&lt;code&gt;Apple silicon&lt;/code&gt; uses unified memory. The CPU and GPU share a single pool of memory, unlike dedicated graphics machines where there are separate VRAM and main memory that have to be moved across a bus.
The biggest advantage of this structure is that many models which would &amp;ldquo;not fit in VRAM at all&amp;rdquo; on dedicated graphics machines can take on a different state on Mac:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It might not be fast,&lt;/li&gt;
&lt;li&gt;But it can probably fit into the memory first.
In other words, Macs are less likely to hit that very rigid &amp;ldquo;VRAM wall&amp;rdquo; right from the start.
This is why many people feel that Macs are particularly suitable for running large models locally as a fallback option. It solves the problem of &amp;ldquo;whether the entire working set can be loaded into the same memory pool.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;but-unified-memory-doesnt-mean-high-speed-for-you&#34;&gt;But Unified Memory Doesn&amp;rsquo;t Mean High Speed for You
&lt;/h2&gt;&lt;p&gt;You must look at this part separately.
What did unified memory solve?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It solved the hard separation between dedicated VRAM and main memory.&lt;/li&gt;
&lt;li&gt;It solved many cases where models couldn&amp;rsquo;t fit directly onto cards with small VRAM.&lt;/li&gt;
&lt;li&gt;It solved some very ugly cross-device data transfers.
But what didn&amp;rsquo;t it solve?&lt;/li&gt;
&lt;li&gt;It didn&amp;rsquo;t change large model inference from &amp;ldquo;massive memory reads&amp;rdquo; to something else.&lt;/li&gt;
&lt;li&gt;It didn&amp;rsquo;t make large models suddenly stop being bandwidth-hungry.&lt;/li&gt;
&lt;li&gt;It didn&amp;rsquo;t automatically give all inference frameworks the mature ecosystem of CUDA.
So, the comfort of Mac is not the same thing as the speed of a high-VRAM NVIDIA card.
Mac is more like:&lt;/li&gt;
&lt;li&gt;Quiet machine operation&lt;/li&gt;
&lt;li&gt;Large total memory&lt;/li&gt;
&lt;li&gt;Unified architecture&lt;/li&gt;
&lt;li&gt;Finally allowing models that previously wouldn&amp;rsquo;t fit to run first.
NVIDIA&amp;rsquo;s high-VRAM card is more like:&lt;/li&gt;
&lt;li&gt;Mature ecosystem&lt;/li&gt;
&lt;li&gt;Complete CUDA toolchain&lt;/li&gt;
&lt;li&gt;When you truly keep the model and cache on the GPU, speed can be much easier to boost.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;why-speed-ultimately-depends-on-nvidias-large-vram&#34;&gt;Why Speed Ultimately Depends on NVIDIA&amp;rsquo;s Large VRAM
&lt;/h2&gt;&lt;p&gt;This is not an emotional judgment, but a practical conclusion drawn after wrestling with local deployment.
If you are pursuing these things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Local assistant always running&lt;/li&gt;
&lt;li&gt;Multi-turn long conversations&lt;/li&gt;
&lt;li&gt;Long context windows&lt;/li&gt;
&lt;li&gt;Higher token/s rate&lt;/li&gt;
&lt;li&gt;Minimizing waiting time as much as possible
Then ultimately, the large VRAM NVIDIA card is what matters. Because what you are truly buying is the ability to keep the model and cache stably on the GPU.
Mac also works, but it is better suited for a different set of requirements:&lt;/li&gt;
&lt;li&gt;I want a machine that can load an even larger model into it first.&lt;/li&gt;
&lt;li&gt;I accept average speed, but I don&amp;rsquo;t want to mess with drivers and peripherals.&lt;/li&gt;
&lt;li&gt;I care more about overall experience, power consumption, and noise.
Both paths are reasonable; they just solve different problems.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;back-to-gemma-4-my-final-verdict&#34;&gt;Back to Gemma 4, My Final Verdict
&lt;/h2&gt;&lt;p&gt;This time with &lt;code&gt;Gemma 4&lt;/code&gt;, I really feel that local open-source models have reached a stage where hardware discussion is more warranted.
But just because the model gets stronger doesn&amp;rsquo;t mean the laws of physics loosen up accordingly.
No matter how strong the &lt;code&gt;31B&lt;/code&gt; model is, it will slow down if VRAM is insufficient.
Even with the practical &lt;code&gt;26B A4B&lt;/code&gt;, long contexts still put pressure on the system.
And while Apple&amp;rsquo;s unified memory is comfortable, it only makes it easier to &amp;ldquo;get something running&amp;rdquo;; it won&amp;rsquo;t give you the speed of a large VRAM CUDA card for free.
So, I&amp;rsquo;ll end with this rather blunt summary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;speed&lt;/strong&gt;, prioritize NVIDIA with large VRAM.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;reliability/fallback&lt;/strong&gt;, Mac&amp;rsquo;s unified memory is indeed comfortable.&lt;/li&gt;
&lt;li&gt;If you plan to run on hardware like the &lt;code&gt;3060 12GB&lt;/code&gt; long-term, don&amp;rsquo;t always aim for dense, massive models; a path like &lt;code&gt;26B A4B&lt;/code&gt; is more realistic.
With this set of articles, I will wrap up here.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$blog-writer Google has released the Gemma4 model after a year. As usual, I&amp;#39;m trying to deploy it locally on that old desktop with an unupgraded NVIDIA 3060 12GB graphics card. This time I caught the initial release, but I couldn&amp;#39;t find an upgraded version of the commonly used Gemma3. However, there is a similar version called GemmaE4b. Please first search and introduce all the models released this time, what the letters in their abbreviations mean, and then search for online reviews about Gemma4. The key point is that Google updated the model&amp;#39;s protocol this time, so the restrictions for users are fewer. The biggest surprise: my usual test question—write a piece of C++ code to output a five-pointed star in the console. Last year&amp;#39;s smaller parameter open-source models couldn&amp;#39;t handle this problem, but Google managed it this time. In the first version, it gave the answer, completely exceeding my expectations. It knew about my trap; outputting a five-pointed star to the console is very difficult, so it directly hardcoded a string of a five-pointed star for direct console output. This is the original text: Because drawing a five-pointed star with precise geometric structure using mathematical logic in a pure text console (Console) is very complex (involving coordinate system transformation and pixel filling), the most classic and visually best method is to use ASCII Art. After I forced it to perform calculations, it also managed it through mathematical calculation, successfully drawing the five-pointed star. Previously, I often used Gemma4 for local translation tasks; many multilingual versions of historical articles on current blogs are like this. The model used for local testing: gemma-4-26b-a4b. The 31b version is indeed too slow. But looking at the reviews, the 31b effect is very good, and its ranking performance is excellent. While browsing forums, I realized that if the VRAM is insufficient and the model parameters are increased, the token generation speed will drop drastically. Can you explain why? Macs don&amp;#39;t have this problem because they use unified memory; please explain the technical reason. Also, if speed is required, then an NVIDIA card with large VRAM is still necessary. The Mac solution can serve as a fallback, but it cannot match the speed. This content is very extensive; please evaluate whether it should be split into a series of articles.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;writing-outline-summary&#34;&gt;Writing Outline Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;The third article will only retain the two threads: &amp;ldquo;Why speed collapses&amp;rdquo; and &amp;ldquo;Why Mac does not equal fast,&amp;rdquo; without revisiting the content of the previous two articles.&lt;/li&gt;
&lt;li&gt;Start by discussing VRAM limitations, then move to non-linear slowdowns; this logic is smoother than the previous version.&lt;/li&gt;
&lt;li&gt;Mac and Nvidia will be discussed as two different dimensions—one focusing on reliability/fallback, and one focusing on speed.&lt;/li&gt;
&lt;li&gt;The conclusion will only retain hardware judgment, without repeating the explanation of why series separation occurred.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Google released Gemma 4 this time (Part II)</title>
        <link>https://ttf248.life/en/p/gemma-4-series-local-test-on-rtx-3060/</link>
        <pubDate>Wed, 08 Apr 2026 23:52:20 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/gemma-4-series-local-test-on-rtx-3060/</guid>
        <description>&lt;p&gt;If you only look at the leaderboard, &lt;code&gt;31B&lt;/code&gt; is definitely the most eye-catching.
But when you actually get the machine out, it&amp;rsquo;s still that un-upgraded &lt;code&gt;RTX 3060 12GB&lt;/code&gt;, and your judgment will change immediately. How should I put it? For local deployment, in the end, it&amp;rsquo;s not about who looks the fanciest, but who seems like the one you can live with long-term. For me, what is truly worth running first this time isn&amp;rsquo;t &lt;code&gt;31B&lt;/code&gt;, but &lt;code&gt;26B A4B&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The previous article &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/gemma-4-series-models-and-license/&#34; &gt;Google released Gemma 4 (Part 1): Don&amp;rsquo;t rush to local deployment; you need to understand the model and protocol first&lt;/a&gt; covered the release and protocols. This current article only talks about the local experience itself; the last one continues with &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/gemma-4-series-vram-cliff-and-mac-unified-memory/&#34; &gt;Google released Gemma 4 (Part 3): Why does running out of VRAM cause a cliff, and why can Mac act as a fallback but is slow&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;why-i-ran-26b-a4b-first&#34;&gt;Why I ran &lt;code&gt;26B A4B&lt;/code&gt; first
&lt;/h2&gt;&lt;p&gt;The reason is actually quite basic: it&amp;rsquo;s about hardware reality.
While &lt;code&gt;31B&lt;/code&gt; is certainly powerful, and the official leaderboards and initial community feedback have been very strong. However, if you run it on a machine like a &lt;code&gt;3060 12GB&lt;/code&gt;, the issue immediately shifts from &amp;ldquo;Is it powerful?&amp;rdquo; to &amp;ldquo;Is it worth waiting for?&amp;rdquo;. Once the model and cache start offloading to system memory, the speed can easily collapse. I will cover this in detail in a third article.
&lt;code&gt;26B A4B&lt;/code&gt; is different.
Although its total parameters are &lt;code&gt;25.2B&lt;/code&gt;, only about &lt;code&gt;3.8B&lt;/code&gt; are actually activated per token. Simply put, it&amp;rsquo;s the one in Gemma 4 that feels most &amp;ldquo;designed for local users.&amp;rdquo;
So, if your machine is similar to mine—an older consumer-grade card—here is a straightforward way to decide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you want to see benchmark scores, go with &lt;code&gt;31B&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you plan to use it locally in the long term, start with &lt;code&gt;26B A4B&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-five-point-star-problem-someone-finally-understood-my-trap-this-time&#34;&gt;The Five-Point Star Problem: Someone Finally Understood My Trap This Time
&lt;/h2&gt;&lt;p&gt;I have always had a rather basic test question: asking the model to write a piece of &lt;code&gt;C++&lt;/code&gt; code that outputs a five-pointed star to the console.&lt;/p&gt;
&lt;p&gt;This problem might look like a joke, but it&amp;rsquo;s actually quite tricky. Many models tend to interpret it as a pure mathematical drawing problem, and then they start talking about coordinates, trigonometric functions, and loops, ultimately outputting a mess of characters in the plain text console that is completely unreadable.&lt;/p&gt;
&lt;p&gt;Last year, many small-parameter open-source models failed at this point.&lt;/p&gt;
&lt;p&gt;My first reaction to &lt;code&gt;Gemma 4&lt;/code&gt; this time was actually quite surprising. It didn&amp;rsquo;t rush to pretend it understood; instead, it first identified the constraints and provided this judgment:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since drawing a five-pointed star with precise geometric structure directly using mathematical logic in a plain text console (Console) is very complex (involving coordinate system transformation and pixel filling), the most classic and visually effective method is to use ASCII Art.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;regarding-the-five-pointed-star-problem-someone-finally-understood-my-trap-this-time&#34;&gt;Regarding the Five-Pointed Star Problem, Someone Finally Understood My Trap This Time
&lt;/h2&gt;&lt;p&gt;To put it plainly, they first understood the environmental constraints behind the problem. The console is not a canvas, and the character grid is not a pixel grid. You must first figure out &amp;ldquo;how to stably output a five-pointed star,&amp;rdquo; before discussing mathematical drawing.
Then, in its first version, it directly provided a hardcoded string for the five-pointed star.
This action was very on point. It wasn&amp;rsquo;t about showing off derivations; it was about getting the problem solved correctly first.&lt;/p&gt;
&lt;h2 id=&#34;what-surprised-me-even-more-is-that-it-could-continue-further&#34;&gt;What surprised me even more is that it could continue further.
&lt;/h2&gt;&lt;p&gt;If it had only stopped at ASCII Art, this problem would have only shown that it recognized the trap.
What really impressed me was that when I continued to ask it to perform mathematical calculations afterward, it didn&amp;rsquo;t falter; instead, it was able to proceed logically, mapping the geometric relationships onto a character grid and finally calculating the pentagram.
This demonstrates not just &amp;ldquo;it can write some code,&amp;rdquo; but rather that it understands this problem has two layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first layer: What is the most stable answer for the console?&lt;/li&gt;
&lt;li&gt;The second layer: If you insist on doing calculations, how do you reduce a geometric problem onto a character grid?
Previously, many local small models would jump straight to the second layer and fail at the first. &lt;code&gt;Gemma 4&lt;/code&gt; reversed this approach this time; it first identified the boundaries and then decided on the solution method.
I think this is more valuable than any single benchmark score.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;this-coding-improvement-isnt-just-about-being-smarter&#34;&gt;This Coding Improvement Isn&amp;rsquo;t Just About Being &amp;ldquo;Smarter&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;The reason this five-star problem is so useful is that it doesn&amp;rsquo;t just test syntax.
What it truly tests is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ability to first understand the output environment.&lt;/li&gt;
&lt;li&gt;The ability to admit when an intuitive solution is inappropriate.&lt;/li&gt;
&lt;li&gt;The ability to switch between achieving &amp;ldquo;optimal presentation effect&amp;rdquo; and fulfilling &amp;ldquo;user-mandated calculation.&amp;rdquo;
Once a model can solve this type of problem correctly, it indicates that the model is starting to act more like a development assistant capable of handling real-world constraints, rather than just one that completes code snippets.
This is also why my first impression of &lt;code&gt;Gemma 4&lt;/code&gt; is much better than last year&amp;rsquo;s batch of smaller open models. Many models from last year were good at chatting, completing, and getting by, but when faced with a problem that had even slight boundary conditions, they tended to show their limitations.
At least Google has addressed this weakness this time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;translating-this-line-cannot-simply-be-stated-as-gemma-4-completely-replaces-everything&#34;&gt;Translating this line cannot simply be stated as &amp;ldquo;Gemma 4 completely replaces everything&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;You brought up a very key point earlier: previously, people often used &lt;code&gt;Gemma&lt;/code&gt; for local translation.
The transition to &lt;code&gt;Gemma 4&lt;/code&gt; isn&amp;rsquo;t actually that linear. This is because Google released &lt;code&gt;TranslateGemma&lt;/code&gt; separately in February 2026, and it was built on the architecture of &lt;code&gt;Gemma 3&lt;/code&gt;.
What does this mean?
It means that if your existing local translation pipeline is already working smoothly, you don&amp;rsquo;t necessarily have to switch everything over to &lt;code&gt;Gemma 4&lt;/code&gt; in the short term. Especially for scenarios with very specific goals—like only needing stable multilingual conversion—a dedicated translation model still has its value.
However, if what you want is a single local model that can reasonably handle translation, Q&amp;amp;A, code, and general text tasks, then a more versatile route like &lt;code&gt;26B A4B&lt;/code&gt; is smoother.
It might not be the most specialized one, but it&amp;rsquo;s more like choosing the &amp;ldquo;good enough main model to get running first&amp;rdquo; option in a real-world scenario.&lt;/p&gt;
&lt;h2 id=&#34;why-i-dont-want-to-keep-praising-31b-in-the-second-article&#34;&gt;Why I Don&amp;rsquo;t Want to Keep Praising &lt;code&gt;31B&lt;/code&gt; in the Second Article
&lt;/h2&gt;&lt;p&gt;It&amp;rsquo;s not that &lt;code&gt;31B&lt;/code&gt; is bad; quite the opposite, it&amp;rsquo;s &lt;em&gt;too&lt;/em&gt; good, which makes it easy to get distracted.
If you keep focusing on the leaderboard performance of &lt;code&gt;31B&lt;/code&gt;, it&amp;rsquo;s easy to write this article as &amp;ldquo;Strong models are truly strong.&amp;rdquo; But what local deployment fears most is exactly that kind of talk. Because what truly determines whether you will continue using it every day isn&amp;rsquo;t the leaderboard, but rather:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the startup too slow?&lt;/li&gt;
&lt;li&gt;Does the response speed drop severely?&lt;/li&gt;
&lt;li&gt;Does long context quickly ruin the experience?&lt;/li&gt;
&lt;li&gt;Can your own machine actually handle it?
On a machine like the &lt;code&gt;3060 12GB&lt;/code&gt;, these practical issues are much more important than the leaderboard.
So, my conclusion for the second article is simple.
&lt;code&gt;31B&lt;/code&gt; is worth looking at; &lt;code&gt;26B A4B&lt;/code&gt; is worth using. For local players, these two statements are not the same thing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;my-initial-local-conclusion&#34;&gt;My Initial Local Conclusion
&lt;/h2&gt;&lt;p&gt;If I had to summarize my experience from this test in one sentence, it would be:
&lt;code&gt;Gemma 4&lt;/code&gt; finally feels like a local model that understands context/scenarios.
Especially the &lt;code&gt;26B A4B&lt;/code&gt;. It might not be the model best for showing off on leaderboards, but under real-world constraints—like older hardware, consumer-grade GPUs, and long-term local use—it actually feels more like the true workhorse choice.
At least with this five-star test, Google has passed.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$blog-writer Google has released the Gemma4 model after a year. As usual, I&amp;#39;m trying to deploy it locally on that old desktop with an unupgraded NVIDIA 3060 12GB graphics card. This time I caught the initial release, but I couldn&amp;#39;t find an upgraded version of the commonly used Gemma3. However, there is a similar version called GemmaE4b. Please search and introduce all the models released this time, what the abbreviation letters mean in them, and then search for online reviews about Gemma4. The key point is that Google updated the model&amp;#39;s protocol this time, and the restrictions for users are fewer. The biggest surprise: my usual test question—write a piece of C++ code to output a five-pointed star in the console. Last year&amp;#39;s smaller open-source models couldn&amp;#39;t handle this problem, but Google managed it this time. In the first version, it gave an answer that completely exceeded my expectations; it knew about my trap. Outputting a five-pointed star to the console is very tricky, so it directly hardcoded a string for the five-pointed star, which was outputted directly to the console. This is the original text: Because drawing a five-pointed star with precise geometric structure using mathematical logic in a pure text console (Console) is very complex (involving coordinate system transformation and pixel filling), the most classic and visually best method is to use ASCII Art. After I forced it to perform calculations, it also succeeded through mathematical calculation, successfully drawing the five-pointed star. Previously, I often used Gemma4 for local translation tasks; many multilingual versions of historical articles on current blogs are like this. The model used for local testing: gemma-4-26b-a4b. The 31b version is indeed too slow. But looking at the reviews, the 31b effect is very good, and its ranking performance is excellent. Also, while browsing forums, I realized that if the VRAM is insufficient and the model parameters are increased, the token generation speed will drop drastically. Can you explain why? Macs don&amp;#39;t have this problem because they use unified memory; please explain the technical reason. Furthermore, if speed is required, only an NVIDIA card with large VRAM will do. The Mac solution can serve as a fallback, but it cannot match the speed. This content is very extensive; please evaluate whether it should be split into a series of articles.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;writing-outline-summary&#34;&gt;Writing Outline Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;The second article will only retain the local experience, and will no longer summarize the first article or explain VRAM principles for the third one.&lt;/li&gt;
&lt;li&gt;First provide the hard judgment on &amp;ldquo;why run 26B A4B first,&amp;rdquo; then expand with the five-star test.&lt;/li&gt;
&lt;li&gt;The five-star question is treated as the main axis because it better illustrates the boundary sense in coding scenarios than benchmark scores.&lt;/li&gt;
&lt;li&gt;The translation task will be given its own section to avoid making &lt;code&gt;Gemma 4&lt;/code&gt; seem like a linear successor to all previous processes.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Google has released Gemma 4 this time (Part 1)</title>
        <link>https://ttf248.life/en/p/gemma-4-series-models-and-license/</link>
        <pubDate>Wed, 08 Apr 2026 23:48:20 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/gemma-4-series-models-and-license/</guid>
        <description>&lt;p&gt;On the day of the initial release, what I originally wanted to do was simple: find an upgraded version corresponding to &lt;code&gt;Gemma 3&lt;/code&gt; and download it to run.
However, after looking around, I was a bit stunned. The familiar naming convention of &lt;code&gt;4B / 12B / 27B&lt;/code&gt; is gone; instead, we have &lt;code&gt;E4B&lt;/code&gt;, &lt;code&gt;26B A4B&lt;/code&gt;, and &lt;code&gt;31B&lt;/code&gt;. How should I put it? This time, what Google truly changed wasn&amp;rsquo;t just the model sizes, but even &amp;ldquo;how you should understand this batch of models.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve broken down these articles into three parts. This current article only clarifies the release information, model names, and protocols; the next one will cover &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/gemma-4-series-local-test-on-rtx-3060/&#34; &gt;Google Released Gemma 4 (Part II): Running Locally on a 3060 12GB, 26B A4B is More Realistic&lt;/a&gt;; and the last one will conclude with &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/gemma-4-series-vram-cliff-and-mac-unified-memory/&#34; &gt;Google Released Gemma 4 (Part III): Why VRAM Insufficiency Causes a Cliff, and Why Mac Can Be a Fallback But Isn&amp;rsquo;t Fast&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;lets-first-clarify-what-was-actually-released-this-time&#34;&gt;Let&amp;rsquo;s first clarify what was actually released this time
&lt;/h2&gt;&lt;p&gt;Last year, &lt;code&gt;Gemma 3&lt;/code&gt; was released on March 12, 2025, and this &lt;code&gt;Gemma 4&lt;/code&gt; was released on April 2, 2026. It is indeed about a year apart.
However, we cannot approach this by asking, &amp;ldquo;Who is the next generation after 27B.&amp;rdquo; The four main sizes provided by the official source are no longer simply categorized by total parameters.
| &lt;code&gt;E2B&lt;/code&gt; | Dense | 2.3B effective, 5.1B including embeddings, 128K context | On-device, ultra-lightweight local |&lt;/p&gt;
&lt;h2 id=&#34;clarify-what-was-actually-released-this-time&#34;&gt;Clarify what was actually released this time
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Model&lt;/th&gt;
					&lt;th&gt;Structure&lt;/th&gt;
					&lt;th&gt;Key Numbers&lt;/th&gt;
					&lt;th&gt;Typical Scenarios&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&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 including embeddings, 128K context&lt;/td&gt;
					&lt;td&gt;The original 4B small model main line&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;clarify-what-was-actually-released-this-time-1&#34;&gt;Clarify what was actually released this time
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Model&lt;/th&gt;
					&lt;th&gt;Structure&lt;/th&gt;
					&lt;th&gt;Key Numbers&lt;/th&gt;
					&lt;th&gt;Typical Scenarios&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&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, approx. 3.8B active, 256K context&lt;/td&gt;
					&lt;td&gt;Consumer GPUs, local deployment, balancing quality and speed&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;clarify-what-was-actually-released-this-time-2&#34;&gt;Clarify what was actually released this time
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Model Size&lt;/th&gt;
					&lt;th&gt;Structure&lt;/th&gt;
					&lt;th&gt;Key Numbers&lt;/th&gt;
					&lt;th&gt;Typical Scenarios&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&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;Aiming for the upper limit, leaderboards, and more stable quality&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;lets-clarify-what-was-actually-released-this-time&#34;&gt;Let&amp;rsquo;s clarify what was actually released this time
&lt;/h2&gt;&lt;p&gt;If you only look at the surface, you might feel that the naming is more confusing. But it&amp;rsquo;s not random; Google is deliberately splitting the three tracks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Small model for on-device use, given to &lt;code&gt;E2B / E4B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Local player track, given to &lt;code&gt;26B A4B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Quality and upper limit track, given to &lt;code&gt;31B&lt;/code&gt;
This is also why many people&amp;rsquo;s first impression might be, &amp;ldquo;The previously familiar upgrade path has been broken.&amp;rdquo; It&amp;rsquo;s not that they didn&amp;rsquo;t release an upgraded version; it&amp;rsquo;s that Google doesn&amp;rsquo;t want to sell products based on just one dimension: total parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;e-and-a-are-not-decorative-letters-this-time&#34;&gt;&amp;lsquo;E&amp;rsquo; and &amp;lsquo;A&amp;rsquo; are not decorative letters this time
&lt;/h2&gt;&lt;p&gt;In this batch of names, the most confusing ones are &lt;code&gt;E4B&lt;/code&gt; and &lt;code&gt;A4B&lt;/code&gt;.
The &amp;lsquo;E&amp;rsquo; in &lt;code&gt;E2B&lt;/code&gt; and &lt;code&gt;E4B&lt;/code&gt; stands for &lt;code&gt;effective parameters&lt;/code&gt;, according to the official documentation. Because these two models use &lt;code&gt;Per-Layer Embeddings&lt;/code&gt;, the total parameter count and the actual effective parameter count are not measured by the same metric. Simply put, Google is reminding you that this is not like the old &amp;ldquo;a simple 4B dense model.&amp;rdquo;
The &amp;lsquo;A&amp;rsquo; in &lt;code&gt;26B A4B&lt;/code&gt; stands for &lt;code&gt;active parameters&lt;/code&gt;. The total size is &lt;code&gt;25.2B&lt;/code&gt;, but only about &lt;code&gt;3.8B&lt;/code&gt; are actually activated per token. This is key to the MoE approach: the total model size is large, but the part that actually participates in computation at runtime is much smaller.
So, even though both names seem to have a &amp;lsquo;4B&amp;rsquo;, their meanings are completely different:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;E4B&lt;/code&gt; is for the small model line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;26B A4B&lt;/code&gt; is a large MoE with &amp;ldquo;activation scale of around 4B&amp;rdquo; during local inference.
This naming convention was indeed awkward at first, but it is closer to the actual deployment experience than before.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;if-you-previously-used-gemma-3-how-to-find-the-corresponding-relationship-this-time&#34;&gt;If you previously used Gemma 3, how to find the corresponding relationship this time
&lt;/h2&gt;&lt;p&gt;I think the easiest place to misjudge with this generation is to treat it as a linear upgrade from &lt;code&gt;Gemma 3&lt;/code&gt;.
If you look at it based on usage habits, you can roughly understand it like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Those who used to focus on &lt;code&gt;4B&lt;/code&gt; for light tasks should now first look at &lt;code&gt;E4B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Those who used to focus on &lt;code&gt;27B&lt;/code&gt; to see the model&amp;rsquo;s upper limit should now look at &lt;code&gt;31B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If you previously wanted to find a balance point on consumer-grade GPUs that is &amp;ldquo;powerful enough but not completely unrunnable,&amp;rdquo; now focus on &lt;code&gt;26B A4B&lt;/code&gt;
If you don&amp;rsquo;t clarify this layer first, local deployment will easily go wrong later. You might complain, &amp;ldquo;Why isn&amp;rsquo;t there the familiar upgraded version?&amp;rdquo; while mistakenly choosing a model that isn&amp;rsquo;t actually suitable for you.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-most-valuable-update-this-time-isnt-the-parameters&#34;&gt;The most valuable update this time isn&amp;rsquo;t the parameters
&lt;/h2&gt;&lt;p&gt;What really made me feel like this release was a &amp;ldquo;finally figured it out&amp;rdquo; moment wasn&amp;rsquo;t the leaderboard, but the license.
The old &lt;code&gt;Gemma&lt;/code&gt; terms weren&amp;rsquo;t unusable, but they always felt a bit awkward. Especially if you care about these things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redistribution&lt;/li&gt;
&lt;li&gt;Distillation or secondary packaging&lt;/li&gt;
&lt;li&gt;Integrating the model into your own product pipeline&lt;/li&gt;
&lt;li&gt;Commercial deployment
You always have to go back and look at how those notices, downstream restrictions, and accompanying agreements in the terms should be handled.
By changing directly to &lt;code&gt;Apache 2.0&lt;/code&gt; this time, &lt;code&gt;Gemma 4&lt;/code&gt; made things much cleaner. The core message is very clear:&lt;/li&gt;
&lt;li&gt;Commercially usable&lt;/li&gt;
&lt;li&gt;Modifiable&lt;/li&gt;
&lt;li&gt;Redistributable
The main obligations are limited to retaining familiar open-source elements like the license, notices, and modification documentation.
Simply put, Google didn&amp;rsquo;t just open-source a model this time; they smoothed out the entire process of &amp;ldquo;whether or not people feel safe using it.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;initial-community-feedback-basically-two-lines&#34;&gt;Initial Community Feedback, Basically Two Lines
&lt;/h2&gt;&lt;p&gt;If you only look at the first week&amp;rsquo;s buzz, there are roughly two main sentiments.&lt;/p&gt;
&lt;p&gt;The first line is that &lt;code&gt;31B&lt;/code&gt; is genuinely capable. The official benchmarks are already very impressive. In the &lt;code&gt;Arena AI&lt;/code&gt; text leaderboard, 31B was ranked among the top open-source models upon release, and it also showed a significant improvement over &lt;code&gt;Gemma 3 27B&lt;/code&gt; on &lt;code&gt;LiveCodeBench v6&lt;/code&gt;. Many people&amp;rsquo;s first reaction is that achieving this level of performance with this size is quite beyond expectations.&lt;/p&gt;
&lt;p&gt;The second line is that &lt;code&gt;26B A4B&lt;/code&gt; seems like a lifeline for local users. It might not be the flashiest flagship model at first glance, but it is very practical. Especially if you aren&amp;rsquo;t running things in a data center, but rather on consumer-grade GPUs, workstations, or even older machines, the local experience tends to fall onto this line.&lt;/p&gt;
&lt;p&gt;Of course, there&amp;rsquo;s a very realistic prerequisite for the initial wave of feedback: the ecosystem is still catching up with the versions. Templates, quantization methods, inference frameworks, and front-end tools—many haven&amp;rsquo;t fully kept pace yet. Therefore, when looking at comments right now, it&amp;rsquo;s best to view them in two layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The core model:&lt;/strong&gt; There has indeed been a big improvement here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Local experience:&lt;/strong&gt; This will continue to be influenced by the maturity of the toolchain.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;my-conclusion-on-the-first-article&#34;&gt;My Conclusion on the First Article
&lt;/h2&gt;&lt;p&gt;If you just want to know what Google actually released this time, one sentence is enough.
&lt;code&gt;Gemma 4&lt;/code&gt; is no longer following the old idea of &amp;ldquo;a line of dense models from small to large,&amp;rdquo; but rather separating three paths: device-side deployment, local deployment, and quality ceiling. The names like &lt;code&gt;E4B&lt;/code&gt;, &lt;code&gt;26B A4B&lt;/code&gt;, and &lt;code&gt;31B&lt;/code&gt; sound strange, but behind them is a very practical division of labor for deployment.
But if you ask me what the biggest change is this time, I still stick to that judgment:
It&amp;rsquo;s not about parameters, nor is it about leaderboards; it&amp;rsquo;s that Google finally put &lt;code&gt;Gemma 4&lt;/code&gt; into an open-source protocol that everyone feels more comfortable actually using.
This step is more important than the numbers on the surface.
In the next article, I won&amp;rsquo;t continue talking about the conference narrative; I&amp;rsquo;ll go straight back to local machines. Still with that unupgraded &lt;code&gt;RTX 3060 12GB&lt;/code&gt;, why was it that my initial focus wasn&amp;rsquo;t on &lt;code&gt;31B&lt;/code&gt;, but on &lt;code&gt;26B A4B&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$blog-writer Google has released the Gemma4 model after a year. As usual, I&amp;#39;m trying to deploy it locally on that old desktop with an unupgraded NVIDIA 3060 12GB graphics card. This time I caught the initial release, but I couldn&amp;#39;t find an upgraded version of the previously used Gemma3. However, there is a similar version called GemmaE4b. Please first search and introduce all the models released this time, what the abbreviation letters mean in them, and then search for online reviews about Gemma4. The key point is that Google updated the model&amp;#39;s protocol this time, so the restrictions for users are fewer. The biggest surprise: my usual test question—write a piece of C++ code to output a five-pointed star in the console. Last year&amp;#39;s smaller parameter open-source models couldn&amp;#39;t handle this problem, but Google managed it this time. In the first version, it gave an answer that completely exceeded my expectations; it knew about my trap. Outputting a five-pointed star to the console is very tricky, so it directly hardcoded a string for the five-pointed star, and the console outputted it directly. This is the original text: Because drawing a five-pointed star with precise geometric structure using mathematical logic in a pure text console (Console) is very complex (involving coordinate system transformation and pixel filling), the most classic and visually best method is to use ASCII Art. After I forced it to perform calculations, it also managed it through mathematical calculation and successfully drew the five-pointed star. Previously, I often used Gemma4 for local translation tasks; many multilingual versions of historical articles on current blogs are like this. The model used for local testing: gemma-4-26b-a4b. The 31b version is indeed too slow. But looking at the reviews, the 31b performs very well, and its ranking scores are excellent. Also, while browsing forums, I realized that if the VRAM is insufficient and the model parameters increase, the token generation speed will drop drastically. Can you explain why? Macs don&amp;#39;t have this problem because they use unified memory; please explain the technical reason. Furthermore, if speed is required, then an NVIDIA card with large VRAM is still necessary. The Mac solution can serve as a fallback, but it cannot match the speed. This content is quite extensive; please evaluate whether it should be split into a series of articles.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;writing-outline-summary&#34;&gt;Writing Outline Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;The first article will only focus on clarifying &amp;ldquo;what was actually released this time&amp;rdquo; and &amp;ldquo;why the protocol is important,&amp;rdquo; avoiding topics that compete with local experience discussions.&lt;/li&gt;
&lt;li&gt;We will separate the model roadmap breakdown and then explain the meaning of the letters, making the logical flow more direct than the previous version.&lt;/li&gt;
&lt;li&gt;For the protocol section, we retained the judgment: &amp;ldquo;What was truly released this time is not the parameters, but the usage restrictions.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Community feedback will only be used for synthesis/conclusion, without preemptively including too many local experience details.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Writing an AI blog post, in the end, still needs to be turned into engineering (Part 3)</title>
        <link>https://ttf248.life/en/p/how-i-split-local-online-and-minimax-models/</link>
        <pubDate>Fri, 03 Apr 2026 21:06:02 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/how-i-split-local-online-and-minimax-models/</guid>
        <description>&lt;p&gt;After going through all the configurations in the repository, I am even more certain about one thing: what matters in the end is not how strong any single model is, but rather who should bear the cost at each layer.&lt;/p&gt;
&lt;p&gt;The most obvious signal is that the currently active &lt;code&gt;published.runtime.json&lt;/code&gt; is still the one generated on April 2, 2026, for &lt;code&gt;minimax-m2&lt;/code&gt;, yet the entry from April 3, 2026, at 16:38, labeled &lt;code&gt;5f17088&lt;/code&gt;, has switched the default provider for &lt;code&gt;blog-style-suite&lt;/code&gt; to the local &lt;code&gt;gemma-4-26b-a4b&lt;/code&gt; in &lt;code&gt;LM Studio&lt;/code&gt;. This might look inconsistent, but it actually isn&amp;rsquo;t; it precisely illustrates that this pipeline has begun to specialize.&lt;/p&gt;
&lt;p&gt;With these articles, the first two have laid out the boundaries. &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/why-blog-writer-had-to-exist/&#34; &gt;The first article&lt;/a&gt; discusses why &lt;code&gt;blog-writer&lt;/code&gt; emerged, and &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/how-blog-style-suite-split-style-and-token-cost/&#34; &gt;the second article&lt;/a&gt; discusses how &lt;code&gt;blog-style-suite&lt;/code&gt; separates style learning from token costs. This final article settles on the most practical question: where should local models, online models, and &lt;code&gt;Minimax&lt;/code&gt; ultimately be placed?&lt;/p&gt;
&lt;h2 id=&#34;training-style-data-not-worth-burning-online-models-at-every-step&#34;&gt;Training Style Data, Not Worth Burning Online Models at Every Step
&lt;/h2&gt;&lt;p&gt;The issue of style data, once you start taking it seriously, quickly becomes a practical problem with tokens.
It&amp;rsquo;s not about whether you &lt;em&gt;want&lt;/em&gt; to save costs; if you don&amp;rsquo;t divide the labor, this whole setup won&amp;rsquo;t run for long.
The most common mistake in the past was letting one online model handle everything.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scraping historical articles&lt;/li&gt;
&lt;li&gt;Performing filtering&lt;/li&gt;
&lt;li&gt;Doing categorization&lt;/li&gt;
&lt;li&gt;Scoring&lt;/li&gt;
&lt;li&gt;Sampling&lt;/li&gt;
&lt;li&gt;Enforcing style&lt;/li&gt;
&lt;li&gt;Finally writing the draft
The biggest problem with doing it this way isn&amp;rsquo;t that &amp;ldquo;the model isn&amp;rsquo;t strong enough,&amp;rdquo; but rather that every step burns the same level of cost.
Looking back now, the truly reasonable approach should be to think in reverse: which steps &lt;em&gt;must&lt;/em&gt; be online, which steps should ideally be localized, and which steps shouldn&amp;rsquo;t even be given to a model at all.
As long as this boundary isn&amp;rsquo;t clear, no matter how powerful the model is, it will just end up helping you repeat a bunch of tasks that could have been pre-processed away.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;local-models-are-better-suited-for-dirty-heavy-and-iterative-tasks&#34;&gt;Local Models are Better Suited for Dirty, Heavy, and Iterative Tasks
&lt;/h2&gt;&lt;p&gt;I am increasingly inclined to define local models as the &amp;ldquo;physical layer&amp;rdquo; for production use.
They might not be the strongest, nor perfect every time, but they are particularly suited for tasks such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Building through repeated runs/iterations&lt;/li&gt;
&lt;li&gt;Multi-round compression experiments on style data&lt;/li&gt;
&lt;li&gt;Re-scanning after configuration changes&lt;/li&gt;
&lt;li&gt;Low-risk recalculation on existing structures&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These types of tasks share a clear commonality.
The value isn&amp;rsquo;t in a single, extremely high-value output, but rather in the ability to run repeatedly, tolerate errors, and ideally avoid paying high costs every single round.
Currently, &lt;code&gt;scripts/blog-style-suite/config.json&lt;/code&gt; has switched to &lt;code&gt;lm-studio-gemma4&lt;/code&gt;, which itself indicates a shift in judgment. It&amp;rsquo;s not that local &lt;code&gt;gemma&lt;/code&gt; is necessarily stronger than online models, but for the production pipeline, we are finally starting to prioritize &amp;ldquo;runnability, frequency of use, and ability to iterate/modify repeatedly.&amp;rdquo;
This point actually aligns with the logic I wrote previously in &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/weaker-models-shouldnt-do-frontier-work/&#34; &gt;Don&amp;rsquo;t force strong tasks onto weak models&lt;/a&gt;.
Local models might not be suitable for writing complex, comprehensive articles from scratch, but they are excellent for handling dirty, heavy, and batch processing tasks. Preprocessing style data is inherently more like this category of task.&lt;/p&gt;
&lt;h2 id=&#34;online-models-are-better-suited-for-the-final-polish-not-for-doing-everything-from-scratch&#34;&gt;Online models are better suited for the final polish, not for doing everything from scratch
&lt;/h2&gt;&lt;p&gt;Just because local models are suitable for the production side doesn&amp;rsquo;t mean online models have no value.
The real value of an online model lies precisely in that final polishing touch.
For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supplementing facts based on the latest information&lt;/li&gt;
&lt;li&gt;Structuring arguments within a larger context&lt;/li&gt;
&lt;li&gt;Handling time-sensitive information that requires internet verification&lt;/li&gt;
&lt;li&gt;Transforming already prepared structured style assets into a publishable article
These tasks require higher demands on expression quality, factual integration, and contextual understanding, making online models more valuable here.
In other words, the powerful model is more like the final few assembly line steps. It&amp;rsquo;s not that it &lt;em&gt;can&amp;rsquo;t&lt;/em&gt; do more upfront work, but if you make it scan from beginning to end, the entire cost structure will quickly become distorted.
This is also why &lt;code&gt;blog-writer&lt;/code&gt; is designed to only read from the published location &lt;code&gt;published.runtime.json&lt;/code&gt;, rather than having to switch providers or re-scan the suite directory while drafting. The lighter the consumption side, the better it is for a more powerful model to focus on finalizing the article.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-significance-of-minimax-its-not-just-another-provider-connection&#34;&gt;The Significance of Minimax: It&amp;rsquo;s Not Just Another Provider Connection
&lt;/h2&gt;&lt;p&gt;Many people who see &lt;code&gt;Minimax&lt;/code&gt; might first think: &amp;ldquo;It&amp;rsquo;s just another model being connected.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t think so.&lt;/p&gt;
&lt;p&gt;The truly valuable aspect of &lt;code&gt;Minimax&lt;/code&gt; is that it has successfully paved the way for &lt;strong&gt;&amp;ldquo;multiple provider outputs consumed by a single publishing contract.&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The change on April 2, 2026, at 10:18 (&lt;code&gt;9f15199&lt;/code&gt;) modified &lt;code&gt;blog-style-suite&lt;/code&gt; to support multi-model configurations, with outputs isolated per provider. Subsequently, the README and runtime structure have consistently emphasized one thing: while the suite can generate many sets of results, only the manually selected &lt;code&gt;published.runtime.json&lt;/code&gt; is actually effective.&lt;/p&gt;
&lt;p&gt;This boundary is extremely important.&lt;/p&gt;
&lt;p&gt;Because once this boundary is clear, the role of &lt;code&gt;Minimax&lt;/code&gt; changes from being &amp;ldquo;something that must be bound within the drafting process&amp;rdquo; to becoming:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Something that can participate in production-side comparisons.&lt;/li&gt;
&lt;li&gt;Something that can be used to generate a runtime version.&lt;/li&gt;
&lt;li&gt;Something that can be compared horizontally with local model artifacts.&lt;/li&gt;
&lt;li&gt;Finally, something whose publication is decided by human judgment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This transforms the provider from a &amp;ldquo;system dependency&amp;rdquo; into a &amp;ldquo;replaceable component.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I believe this is the most interesting significance of &lt;code&gt;Minimax&lt;/code&gt; within this engineering setup. It isn&amp;rsquo;t here to dominate the entire pipeline; it&amp;rsquo;s here to validate whether this pipeline has successfully cleaned up its interfaces.&lt;/p&gt;
&lt;h2 id=&#34;true-specialization-is-not-based-on-model-strength-but-on-task-type&#34;&gt;True specialization is not based on model strength, but on task type
&lt;/h2&gt;&lt;p&gt;I now favor a classification method that is quite rudimentary, but very effective.&lt;/p&gt;
&lt;h3 id=&#34;rules-and-hard-constraints&#34;&gt;Rules and Hard Constraints
&lt;/h3&gt;&lt;p&gt;Leave to local scripts.
If it can be solved with deterministic tools like &lt;code&gt;scanner.py&lt;/code&gt;, &lt;code&gt;write_post.py&lt;/code&gt;, or &lt;code&gt;write_post_series.py&lt;/code&gt;, don&amp;rsquo;t let the model get involved.&lt;/p&gt;
&lt;h3 id=&#34;style-data-generation&#34;&gt;Style Data Generation
&lt;/h3&gt;&lt;p&gt;Prioritize local models or lower-cost providers.
Because what is most important here is reproducibility, room for iteration/error, and cacheability, not necessarily the most dazzling single output.&lt;/p&gt;
&lt;h3 id=&#34;final-drafting-and-fact-consolidation&#34;&gt;Final Drafting and Fact Consolidation
&lt;/h3&gt;&lt;p&gt;Hand this off to a model better suited for long-context integration, expression consolidation, and fact-checking/web retrieval.
This layer is where spending money on online models is most worthwhile.
When broken down like this, many previously confusing issues are actually not that complex. You don&amp;rsquo;t need to argue every day about &amp;ldquo;which model is the strongest&amp;rdquo;; you just need to ask: which layer does this task belong to?&lt;/p&gt;
&lt;h2 id=&#34;ultimately-what-is-most-valuable-is-not-the-model-but-the-clear-boundaries&#34;&gt;Ultimately, what is most valuable is not the model, but the clear boundaries.
&lt;/h2&gt;&lt;p&gt;This concludes my third article.
As &lt;code&gt;blog-writer&lt;/code&gt; and &lt;code&gt;blog-style-suite&lt;/code&gt; have evolved, I feel that what is most valuable is not which provider we connected next, or who we replaced, or which one we tested.
What is most valuable is that the boundaries are finally becoming clearer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;blog-writer&lt;/code&gt; handles the consumption side.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;blog-style-suite&lt;/code&gt; handles the production side.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;published.runtime.json&lt;/code&gt; is the publishing point.&lt;/li&gt;
&lt;li&gt;Local models are better suited for dirty and heavy lifting that needs to be run repeatedly.&lt;/li&gt;
&lt;li&gt;Online models are better suited for the final polish/wrap-up.&lt;/li&gt;
&lt;li&gt;Online providers like &lt;code&gt;Minimax&lt;/code&gt; feel more like replaceable components rather than the central hub of the system.
Once the boundaries are clear, the entire workflow flows smoothly.
You won&amp;rsquo;t expect one model package to conquer everything, nor will you stack every step onto the most expensive layer. In the end, while it looks like selecting a model, what we are actually doing is assigning workstations for different types of tasks.
Simply put, having a single strong point is certainly good.
But in the long run, clear boundaries are often more important and stronger than any single point solution.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Repository Commit: &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;Repository Commit: &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;Repository File: &lt;code&gt;scripts/blog-style-suite/config.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Effective Runtime: &lt;code&gt;.agents/data/blog-writing/published.runtime.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Related Old Article: &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/a-long-period-of-deep-ai-programming/&#34; &gt;A Period of Heavy AI Programming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Related Old Article: &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/ultimately-its-returning-to-domestic-models/&#34; &gt;Ultimately Returning to Domestic Models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Related Old Article: &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/weaker-models-shouldnt-do-frontier-work/&#34; &gt;Don&amp;rsquo;t Force Strong Tasks with Weak Models&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$blog-writer This content is quite extensive, so I&amp;#39;ve split it into a series of articles: Last year, many drafts were written using large models. Back then, the process was to create an outline or a list of questions myself, and then have the AI generate the draft, copy the content into a local md document, fill in header information, tag information, and publish the article; recently, I used Codex a lot and found that its web search capability is very strong. So, could I write a skill to automate these tasks? This led to the first draft of the skill blog-writer. I also thought about having the AI learn my previous writing style, which caused blog-writer to consume a lot of tokens when running. Subsequently, I optimized blog-writer in several versions, splitting out the data module and the data generation module. The original data generation module was still an independent skill. As I continued writing, I realized that it would be better as a Python project, which led to blog-style-suite. Then, I found that training on style data also consumes a lot of tokens, so I wanted to use a local large model and connected to a local LLM. I then thought about comparing the differences between the local LLM and the online version, so I integrated minimax; the evolution history of blog-style-suite and blog-writer can be analyzed from the git commit history. Additionally, based on the code for local blog-writer and blog-style-suite, I can discuss the design ideas, how token saving was achieved, and how the data structure was designed—the core design concepts. If tokens are abundant, it can consume entire historical articles; preprocessing can save a lot of tokens.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;writing-strategy-summary&#34;&gt;Writing Strategy Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;The third article will no longer repeat the discussion on architecture, but instead focus solely on the practical issue of &amp;ldquo;model specialization/division of labor.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Start directly by stating the current reality—whether to use &lt;code&gt;published.runtime.json&lt;/code&gt; from the current repository or if it&amp;rsquo;s switched locally to &lt;code&gt;gemma4&lt;/code&gt; via &lt;code&gt;minimax-m2&lt;/code&gt; or &lt;code&gt;config.json&lt;/code&gt;—to reduce filler content.&lt;/li&gt;
&lt;li&gt;The focus should not be on proving which model is stronger, but rather on explaining &lt;em&gt;why&lt;/em&gt; different tasks should be assigned to different cost layers.&lt;/li&gt;
&lt;li&gt;Placing &lt;code&gt;Minimax&lt;/code&gt; in the &amp;ldquo;replaceable provider&amp;rdquo; section aims to pull its significance back into the engineering boundary, rather than treating it as just another entry on a model leaderboard.&lt;/li&gt;
&lt;li&gt;Conclude by returning to the overarching judgment: &amp;ldquo;Clear boundaries are more important than single points of strength,&amp;rdquo; serving as the closing statement for the entire series of articles.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Making the &#34;AI writes blog&#34; thing into an engineering project later (Part II)</title>
        <link>https://ttf248.life/en/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/en/p/how-blog-style-suite-split-style-and-token-cost/</guid>
        <description>&lt;p&gt;If there are enough tokens, the least effort method is actually quite crude: just feed the model historical articles and let it learn on its own.
The problem with this method is that it only suits occasional writing, not continuous work. If you treat blogging as a long-term workflow, relying solely on raw historical articles will quickly go from &amp;ldquo;simple and direct&amp;rdquo; to &amp;ldquo;expensive and messy.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;With these articles, the main thread has shifted. The previous article, &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/why-blog-writer-had-to-exist/&#34; &gt;AI Writing Blogs: It Eventually Needs to Become an Engineering Process (Part 1): Why a blog-writer is inevitable&lt;/a&gt;, discussed automation on the consumption side. This article starts discussing the production side—how to generate style data, how to compress it, and how not to waste tokens; the next article will continue this in &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/how-i-split-local-online-and-minimax-models/&#34; &gt;AI Writing Blogs: It Eventually Needs to Become an Engineering Process (Part 3): Local Models, Online Models, and Minimax—How They Finally Divide Labor&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-most-natural-initial-thought-is-to-just-feed-it-historical-articles&#34;&gt;The most natural initial thought is to just feed it historical articles.
&lt;/h2&gt;&lt;p&gt;This path feels too natural.
If you want the model to learn your writing style, the most intuitive way is certainly to feed it old articles. It&amp;rsquo;s best to include all the posts from your history that are most like your own, and let it summarize them itself.
For a single task, this approach has no flaws.
In fact, many times the results are quite good. If the context is long enough, the model is powerful enough, and there are enough historical articles, the style can indeed be captured.
But the problem isn&amp;rsquo;t &amp;ldquo;can it write this one article,&amp;rdquo; the problem is &amp;ldquo;for the next one, the one after that, do we have to repeat this process?&amp;rdquo;
Feeding a new batch of old articles every time brings several very practical side effects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The same batch of material repeatedly occupies the context window.&lt;/li&gt;
&lt;li&gt;Token overhead grows almost linearly with the number of drafts written.&lt;/li&gt;
&lt;li&gt;The model sees more and more noise, causing genuinely useful signals to become diluted.&lt;/li&gt;
&lt;li&gt;The drafting action and style maintenance action become completely bound together; neither can be easily reduced.
In other words, when tokens are abundant, eating it raw certainly works. But from an engineering perspective, we cannot keep doing that forever.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;this-is-also-why-the-data-module-and-the-data-generation-module-must-be-separated&#34;&gt;This is also why the data module and the data generation module must be separated
&lt;/h2&gt;&lt;p&gt;I later realized that the core idea can be summarized in one sentence: separating the consumption side from the production side.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;blog-writer&lt;/code&gt; is responsible for the consumption side. It only reads an already published runtime and then writes out the article according to a fixed contract.&lt;/p&gt;
&lt;p&gt;Meanwhile, scanning, filtering, scoring, compressing style data, and provider comparison—all of this should be placed in another production pipeline. This is what later became &lt;code&gt;blog-style-suite&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Looking at the git history, this turning point is very clear.&lt;/p&gt;
&lt;p&gt;The commit &lt;code&gt;84a06b5&lt;/code&gt; on April 1, 2026, at 21:47 clearly replaced the original &lt;code&gt;blog-style-maintainer&lt;/code&gt; skill with a repository-level CLI tool. This action speaks volumes because once you have &lt;code&gt;scan/build/rebuild&lt;/code&gt;, an output directory, and a recovery mechanism, it&amp;rsquo;s no longer like a simple skill; it&amp;rsquo;s more like a normal Python project.&lt;/p&gt;
&lt;p&gt;By the commit &lt;code&gt;9e92b8e&lt;/code&gt; on April 1, 2026, at 23:05, &lt;code&gt;blog-style-suite&lt;/code&gt; was further broken down into modules like &lt;code&gt;scanner.py&lt;/code&gt;, &lt;code&gt;builder.py&lt;/code&gt;, and &lt;code&gt;compressor.py&lt;/code&gt;. By this stage, the thinking process was already highly engineered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scanner.py&lt;/code&gt; is responsible for scanning articles from disk and extracting structured features.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;builder.py&lt;/code&gt; is responsible for scoring, selecting, caching, and runtime assembly.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compressor.py&lt;/code&gt; is responsible for the compression steps that involve the model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This represents a completely different approach compared to simply writing a super prompt.&lt;/p&gt;
&lt;h2 id=&#34;saving-tokens-not-by-magic-but-by-preprocessing-and-batching&#34;&gt;Saving Tokens: Not by Magic, But by Preprocessing and Batching
&lt;/h2&gt;&lt;p&gt;The most valuable part of this entire engineering setup, I think, is the commit &lt;code&gt;bc4b950&lt;/code&gt; from April 2, 2026, at 19:41.
That commit was very direct: it reduced AI calls from about &lt;code&gt;2000&lt;/code&gt; times down to a maximum of &lt;code&gt;5&lt;/code&gt; times per provider.
How was this achieved?
It wasn&amp;rsquo;t by &amp;ldquo;making the prompt smarter,&amp;rdquo; but by doing the necessary preprocessing beforehand.
The current flow in &lt;code&gt;blog-style-suite&lt;/code&gt; is very clear:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;scan&lt;/code&gt; stage is purely heuristic, requiring 0 AI calls.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;build&lt;/code&gt; stage first performs heuristic scoring, also requiring 0 AI calls.&lt;/li&gt;
&lt;li&gt;Then, it performs one batch selection and labeling for each of the four lanes: &lt;code&gt;technical / finance / essay / tooling&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally, there is one author style compression step.
Counting this up, the cold start requires at most 5 calls.
More critically, these 5 calls are not spread across every single article; they are concentrated on high-value summary materials that have already been preprocessed.
This is where preprocessing truly saves tokens. It&amp;rsquo;s not about saving a few words; it&amp;rsquo;s about changing the process from &amp;ldquo;calling per article&amp;rdquo; to &amp;ldquo;batch calling by stage.&amp;rdquo;
Furthermore, caching has been implemented.
In &lt;code&gt;builder.py&lt;/code&gt;, there are lane batch fingerprints, provider checkpoint recovery, and contractions like &lt;code&gt;review_pool_per_lane = 12&lt;/code&gt; for local model context. If you change a small amount of data, the entire pipeline doesn&amp;rsquo;t need to rerun.
These kinds of designs might not look flashy, but every single one is highly practical because they solve the problem of &amp;ldquo;don&amp;rsquo;t let the same batch of tokens burn twice.&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;essentially-this-data-structure-is-compressing-the-truly-useful-signal&#34;&gt;Essentially, this data structure is compressing the truly useful signal.
&lt;/h2&gt;&lt;p&gt;Once this is broken down, the data structure will be smooth.
I am now more willing to understand it as three layers.&lt;/p&gt;
&lt;h3 id=&#34;layer-one-scanjson&#34;&gt;Layer One: &lt;code&gt;scan.json&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;This is the shared raw material.
It contains structured signals such as article path, title, date, category, tags, opening paragraph, closing stub, headings, screening results, and lane classification.
It is not directly consumed by &lt;code&gt;blog-writer&lt;/code&gt;; rather, it is passed to the production side for further processing.&lt;/p&gt;
&lt;h3 id=&#34;second-layer-providersourcejson&#34;&gt;Second Layer: &lt;code&gt;{provider}.source.json&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;This is the provider-level checkpoint.
Building upon the shared raw materials, it includes intermediate states such as scoring results, lane selection, fingerprint, and cache status. In other words, it is more like a &amp;ldquo;semi-finished product during processing,&amp;rdquo; with an emphasis on being recoverable, reusable, and resumable.&lt;/p&gt;
&lt;h3 id=&#34;layer-three-providerruntimejson-and-publishedruntimejson&#34;&gt;Layer Three: &lt;code&gt;{provider}.runtime.json&lt;/code&gt; and &lt;code&gt;published.runtime.json&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;This is what the consumption side truly cares about—the finished product.
It retains:&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;
In essence, it compresses a large collection of historical articles into one ready-to-consume runtime style asset.
The &lt;code&gt;published.runtime.json&lt;/code&gt; in particular is crucial for the publishing stage. The &lt;code&gt;blog-writer&lt;/code&gt; only reads this file and does not need to scan &lt;code&gt;content/post&lt;/code&gt;, nor does it need to care about the complete images of all providers in the suite directory.
Once this boundary is established, the consumption side becomes much lighter. The writing model no longer sees a pile of raw old articles, but rather a pre-processed, high-density signal.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;not-everything-should-be-left-to-the-model&#34;&gt;Not Everything Should Be Left to the Model
&lt;/h2&gt;&lt;p&gt;I&amp;rsquo;m increasingly feeling that the most correct judgment in this entire engineering process isn&amp;rsquo;t &amp;ldquo;add more models,&amp;rdquo; but rather &amp;ldquo;don&amp;rsquo;t throw tasks that shouldn&amp;rsquo;t be done by a model onto it.&amp;rdquo;
Things like these are much better handled by local rules first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frontmatter parsing&lt;/li&gt;
&lt;li&gt;Extracting introductory paragraphs&lt;/li&gt;
&lt;li&gt;Headings extraction&lt;/li&gt;
&lt;li&gt;Determining author/repost/model attribution&lt;/li&gt;
&lt;li&gt;Detecting blockquote ratios&lt;/li&gt;
&lt;li&gt;Hard rule filtering for things like &lt;code&gt;&amp;lt;!--more--&amp;gt;&lt;/code&gt;, embedded prompts, and body length.
Having the model do these tasks isn&amp;rsquo;t impossible, but it&amp;rsquo;s wasteful.
What models are better suited for are parts that involve ambiguity or trade-offs. For example, which few articles in a lane best represent the current sentiment, or extracting author style tags from high-scoring articles.
Therefore, what makes &lt;code&gt;blog-style-suite&lt;/code&gt; truly valuable isn&amp;rsquo;t just &amp;ldquo;saving tokens,&amp;rdquo; but rather its re-division of labor among humans, rules, and models—assigning each party the tasks they are best suited for.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;preprocessing-isnt-about-saving-a-few-tokens-its-about-making-the-act-of-writing-sustainable&#34;&gt;Preprocessing isn&amp;rsquo;t about saving a few tokens; it&amp;rsquo;s about making the act of writing sustainable.
&lt;/h2&gt;&lt;p&gt;For the conclusion in the second article, I want to make it more direct.&lt;/p&gt;
&lt;p&gt;When you have plenty of tokens, reading historical articles raw is fine. In fact, if you only write one or two pieces, it might even be less mentally taxing.&lt;/p&gt;
&lt;p&gt;But as soon as you want to turn this into a long-term workflow, preprocessing becomes non-negotiable. Because without preprocessing, the writing model has to re-read old materials every time, and style maintenance and article generation are always mixed together.&lt;/p&gt;
&lt;p&gt;The significance of &lt;code&gt;blog-style-suite&lt;/code&gt; is to untangle this mess.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not about making the system look complex, nor is it just for another project name; it&amp;rsquo;s so that &lt;code&gt;blog-writer&lt;/code&gt; can remain lightweight, stable, and focused on &amp;ldquo;only the action of writing.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Having reached this point, the next question naturally follows.&lt;/p&gt;
&lt;p&gt;Since the production side has been separated, what model should bear this cost? Local models, online models, or &lt;code&gt;Minimax&lt;/code&gt;—where should each one stand in the workflow? I&amp;rsquo;ll save this for the next article: &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/how-i-split-local-online-and-minimax-models/&#34; &gt;AI Writing Blogs: How It Eventually Has to Become Engineering (Part 3): The Division of Labor Between Local Models, Online Models, and Minimax&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Repository Commit: &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;Repository Commit: &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;Repository Commit: &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;Repository File: &lt;code&gt;scripts/blog-style-suite/README.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repository File: &lt;code&gt;scripts/blog-style-suite/style_pipeline/scanner.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repository File: &lt;code&gt;scripts/blog-style-suite/style_pipeline/builder.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repository File: &lt;code&gt;scripts/blog-style-suite/style_pipeline/compressor.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Effective Runtime: &lt;code&gt;.agents/data/blog-writing/published.runtime.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$blog-writer This content is quite extensive, so I&amp;#39;ve split it into a series of articles: Last year, many drafts were written using large models. Back then, the process was to create an outline or a list of questions myself, and then have the AI generate the draft, copy the content into a local md document, fill in header information, tag information, and publish the article; recently, I used Codex a lot and found that its web search capability is very strong. So, could I write a skill to automate these tasks? This led to the first draft of the skill blog-writer. I also thought about having the AI learn my previous writing style, which caused blog-writer to consume a lot of tokens when running. Subsequently, I optimized blog-writer in several versions, splitting out the data module and the data generation module. The original data generation module was still an independent skill. As I continued writing, I realized that it would be better as a Python project, which led to blog-style-suite. Then, I found that training on style data also consumes a lot of tokens, so I wanted to use a local large model and connected to a local LLM. I then thought about comparing the differences between the local LLM and the online version, so I integrated minimax; the evolution history of blog-style-suite and blog-writer can be analyzed from the git commit history. Additionally, based on the code for local blog-writer and blog-style-suite, I can discuss the design ideas, how token saving was achieved, and how the data structure was designed—the core design concepts. If tokens are abundant, it can consume entire historical articles; preprocessing can save a lot of tokens.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;writing-outline-summary&#34;&gt;Writing Outline Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;This article shifts the focus from the act of writing drafts to data engineering, with the core answer being &amp;ldquo;why modularization is necessary.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;The introduction directly acknowledges that &amp;ldquo;using raw historical articles works,&amp;rdquo; which makes the subsequent arguments for splitting more convincing.&lt;/li&gt;
&lt;li&gt;It elaborates on the three structural layers: &lt;code&gt;scan.json&lt;/code&gt;, &lt;code&gt;source.json&lt;/code&gt;, and &lt;code&gt;runtime.json&lt;/code&gt;, avoiding vague architectural discussions.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bc4b950&lt;/code&gt; is placed in the middle as a turning point because &amp;ldquo;reducing from about 2000 times to 5 times&amp;rdquo; best illustrates the value of preprocessing.&lt;/li&gt;
&lt;li&gt;The conclusion re-separates the consumption side and the production side, setting the stage for model division in the third article.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>AI Writing a Blog: The Next Steps Towards Engineering (Part 1)</title>
        <link>https://ttf248.life/en/p/why-blog-writer-had-to-exist/</link>
        <pubDate>Fri, 03 Apr 2026 20:58:02 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/why-blog-writer-had-to-exist/</guid>
        <description>&lt;p&gt;I wrote quite a few AI articles last year. The most basic workflow back then was: first, organize an outline or a list of questions myself; let the large model spit out the main body text; then copy the content into a local &lt;code&gt;md&lt;/code&gt; document, add frontmatter, tags, categories, and titles, and finally publish it.
This process isn&amp;rsquo;t unusable, but it&amp;rsquo;s tedious. The part that really wastes time isn&amp;rsquo;t the main body text, but the repetitive labor surrounding it. Especially after using &lt;code&gt;Codex&lt;/code&gt; a lot recently, this awkwardness has become even stronger. It can read repositories, modify files, supplement materials, and even write articles directly into the directory. If I still have to copy and paste things manually, it feels like I&amp;rsquo;m tying down the tool&amp;rsquo;s legs.&lt;/p&gt;
&lt;p&gt;This series of articles is actually trying to convey one thing: AI writing blogs cannot rely solely on a single prompt in the long run. This current article first discusses why &lt;code&gt;blog-writer&lt;/code&gt; came into existence; the next article will continue with &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/how-blog-style-suite-split-style-and-token-cost/&#34; &gt;AI Writing Blogs: Later, It Still Needs to Be Engineered (Part II): How blog-style-suite Separates Style Learning and Token Costs&lt;/a&gt;; and the last article concludes with &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/how-i-split-local-online-and-minimax-models/&#34; &gt;AI Writing Blogs: Later, It Still Needs to Be Engineered (Part III): How Local Models, Online Models, and Minimax Will Finally Divide Labor&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;whats-truly-annoying-isnt-writing-the-draft-but-that-sequence-of-mechanical-actions&#34;&gt;What&amp;rsquo;s truly annoying isn&amp;rsquo;t writing the draft, but that sequence of mechanical actions.
&lt;/h2&gt;&lt;p&gt;The early workflow was essentially like an outsourced assembly line.
I would first list out the problems clearly, or build a rough outline. The model is responsible for laying out the main body text. Then, a human comes back to complete the remaining publishing steps.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Copy to local &lt;code&gt;md&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Fill in &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;date&lt;/code&gt;, and &lt;code&gt;slug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add tags and categories&lt;/li&gt;
&lt;li&gt;Insert &lt;code&gt;&amp;lt;!--more--&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Organize reference materials&lt;/li&gt;
&lt;li&gt;Finally, decide which directory it should go into
Looking at each step individually, this sequence isn&amp;rsquo;t difficult. But when strung together, it becomes tedious. What&amp;rsquo;s annoying isn&amp;rsquo;t the technical difficulty; it&amp;rsquo;s that these steps are all mechanical, yet they cannot be skipped.
This is why I increasingly feel that changes like &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/command-line-ai-coding-interaction/&#34; &gt;AI coding interaction based on command line&lt;/a&gt; are not just about &amp;ldquo;changing the entry point.&amp;rdquo; When AI can directly read and write files within a repository, if blog writing still stops at the level of &amp;ldquo;copying the body text to a local document,&amp;rdquo; the entire workflow is actually outdated.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;blog-writer-the-first-layer-of-value-its-not-the-style-its-locking-down-the-contract&#34;&gt;blog-writer The First Layer of Value: It&amp;rsquo;s Not the Style, It&amp;rsquo;s Locking Down the Contract
&lt;/h2&gt;&lt;p&gt;The very first node for &lt;code&gt;blog-writer&lt;/code&gt; was at 17:00 on April 1, 2026, with the commit hash &lt;code&gt;991536a&lt;/code&gt;. Looking at the git commit history, this version included &lt;code&gt;SKILL.md&lt;/code&gt;, &lt;code&gt;write_post.py&lt;/code&gt;, and an initial set of style guidelines all together.&lt;/p&gt;
&lt;p&gt;However, when I looked back later, the most valuable part of this draft wasn&amp;rsquo;t that &amp;ldquo;the AI learned my writing style,&amp;rdquo; but rather that it established a rigid contract for content creation.&lt;/p&gt;
&lt;p&gt;What does &amp;ldquo;locking down the contract&amp;rdquo; mean?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The input must include at least an outline and factual anchors.&lt;/li&gt;
&lt;li&gt;The output must be complete Markdown, not a work in progress.&lt;/li&gt;
&lt;li&gt;Frontmatter cannot rely on manual additions anymore.&lt;/li&gt;
&lt;li&gt;The article cannot just stay in the chat window; it must land directly into &lt;code&gt;content/post&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This point is crucial because prompts themselves are inherently unstable. If you say, &amp;ldquo;Write it like before,&amp;rdquo; today, it might understand that only the tone should be similar; if you repeat it tomorrow, it might only learn superficial sentence structures. But once it&amp;rsquo;s written as a &lt;code&gt;Skill&lt;/code&gt;, the rule shifts from &amp;ldquo;improvisation&amp;rdquo; to a &amp;ldquo;fixed workflow.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The subsequent nodes were actually all about reinforcing this contract.&lt;/p&gt;
&lt;p&gt;The commit at 22:54 on April 2, 2026, with hash &lt;code&gt;8eb735a&lt;/code&gt;, standardized elements like author fields, writing notes, and original prompts. By this stage, the blog draft was no longer considered &amp;ldquo;finished once the body text is done&amp;rdquo;; instead, metadata, traceability, and public notes were all standardized together.&lt;/p&gt;
&lt;p&gt;Therefore, the first layer of value for &lt;code&gt;blog-writer&lt;/code&gt; has never been about making the model &lt;em&gt;seem&lt;/em&gt; better at writing; it&amp;rsquo;s about finally giving the act of drafting repeatable boundaries.&lt;/p&gt;
&lt;h2 id=&#34;series-mode-which-is-actually-one-step-forward-in-writing-workflow&#34;&gt;Series Mode, Which is Actually One Step Forward in Writing Workflow
&lt;/h2&gt;&lt;p&gt;After stabilizing writing a single article, the next problem quickly emerged.
Some topics are simply not suitable to be crammed into one piece. If you force it, the result often becomes a long article that is information-heavy, has a scattered main thread, and fails to fully explain every point.
This is why the commit &lt;code&gt;1a5604e&lt;/code&gt; on April 2, 2026, at 23:55 was so crucial. That time, they directly added the series mode along with &lt;code&gt;write_post_series.py&lt;/code&gt;. The articles are linked using &lt;code&gt;relref&lt;/code&gt;, and the replacement is done uniformly during batch writing.
This might look like a minor upgrade to a file-writing script, but it&amp;rsquo;s not.
It illustrates one thing: content engineering is no longer just about &amp;ldquo;how to generate this single article,&amp;rdquo; but rather starts considering &amp;ldquo;how to stably save this set of content, how to guarantee the order, and how to link between them on the site.&amp;rdquo;
The next day&amp;rsquo;s commit, &lt;code&gt;04dccb9&lt;/code&gt; on April 3, 2026, at 09:29, pushed this process one step further. The timestamps for series articles now increment by minutes instead of sharing a single timestamp. This change is small but very &amp;ldquo;engineering-y&amp;rdquo; because it solves real problems like Hugo list pages, previous/next article navigation, and series ordering.
Simply put, the series mode isn&amp;rsquo;t about looking advanced; it&amp;rsquo;s about eliminating the need for manual fixes when publishing multiple articles together.&lt;/p&gt;
&lt;h2 id=&#34;but-relying-on-just-one-skill-will-eventually-hit-a-token-wall&#34;&gt;But relying on just one skill will eventually hit a token wall.
&lt;/h2&gt;&lt;p&gt;The problem lies here.
Once you start seriously tinkering with style learning, the context of &lt;code&gt;blog-writer&lt;/code&gt; will quickly become bloated. You not only want it to write, but you also want it to write like you used to. The most natural way to do this is to dump all your historical articles into it.
This works for a single run, of course.
But as soon as you&amp;rsquo;re not writing an occasional piece, but trying to make it a long-term workflow, the problems immediately arise:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;High token consumption&lt;/li&gt;
&lt;li&gt;Repeatedly feeding the same batch of old articles every time&lt;/li&gt;
&lt;li&gt;Model attention is diluted by old material&lt;/li&gt;
&lt;li&gt;Drafting and style maintenance are intertwined; neither is easy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It was from here that I slowly realized that &lt;code&gt;blog-writer&lt;/code&gt; is better suited for the consumption side, rather than trying to feed it everything.
The act of drafting should be as light, direct, and limited to reading only the effective versions as possible. As for how to generate, filter, or compress style data, that&amp;rsquo;s a matter for a separate production pipeline. This realization finally pushed me to the next step, which was &lt;a class=&#34;link&#34; href=&#34;https://ttf248.life/en/p/how-blog-style-suite-split-style-and-token-cost/&#34; &gt;AI Blog Writing: It Still Needs to Be Engineered (Part II): How blog-style-suite Separates Style Learning from Token Costs&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;first-stabilize-the-process-only-then-can-we-talk-about-style-and-models&#34;&gt;First, stabilize the process; only then can we talk about style and models.
&lt;/h2&gt;&lt;p&gt;Looking back now, &lt;code&gt;blog-writer&lt;/code&gt; didn&amp;rsquo;t emerge because I suddenly wanted to build a blog writing assistant.
It was more because the original workflow started failing to keep up with new ways of working.
Once a tool like &lt;code&gt;Codex&lt;/code&gt; can connect to the internet for supplementary material, read and write within repositories, and directly call scripts, the act of writing a blog shouldn&amp;rsquo;t stop at &amp;ldquo;copying the body text to a local document.&amp;rdquo; If you don&amp;rsquo;t automate this part, it will actually become the clumsiest link in the entire chain.
So, I&amp;rsquo;ll leave the conclusion for the first post here.
What &lt;code&gt;blog-writer&lt;/code&gt; solved initially wasn&amp;rsquo;t writing style, but the repetitive labor of the publishing action. Without this layer of contract, any subsequent discussion about tokens, data structures, or local models is actually baseless.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Repository Commit: &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;Repository Commit: &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;Repository Commit: &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;Repository Commit: &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;Repository File: &lt;code&gt;.agents/skills/blog-writer/SKILL.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repository File: &lt;code&gt;.agents/skills/blog-writer/scripts/write_post.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repository File: &lt;code&gt;.agents/skills/blog-writer/scripts/write_post_series.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$blog-writer This content is quite extensive, so I&amp;#39;ve split it into a series of articles: Last year, many drafts were written using large models. Back then, the process was to create an outline or a list of questions myself, and then have the AI generate the draft, copy the content into a local md document, fill in header information, tag information, and publish the article; recently, I used Codex a lot and found that its web search capability is very strong. So, could I write a skill to automate these tasks? This led to the first draft of the skill blog-writer. I also thought about having the AI learn my previous writing style, which caused blog-writer to consume a lot of tokens when running. Subsequently, I optimized blog-writer in several versions, splitting out the data module and the data generation module. The original data generation module was still an independent skill. As I continued writing, I realized that it would be better as a Python project, which led to blog-style-suite. Then, I found that training on style data also consumes a lot of tokens, so I wanted to use a local large model and connected to one locally. I then thought about comparing the differences between the local large model and the online version, so I integrated minimax. The evolution history of blog-style-suite and blog-writer can be analyzed from the git commit history. Additionally, based on the code for local blog-writer and blog-style-suite, I can discuss the design ideas, how token saving was achieved, and how the data structure was designed—the core design concepts. If tokens are abundant, it can consume entire historical articles; preprocessing can save a lot of tokens.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;writing-strategy-summary&#34;&gt;Writing Strategy Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;The first article should focus on the workflow trigger point, without rushing to detail the division of labor between tokens and models, to avoid having all three articles compete for the main narrative.&lt;/li&gt;
&lt;li&gt;It retains the key insight: &amp;ldquo;The body content is not difficult; what&amp;rsquo;s troublesome are the mechanical actions before and after publishing.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;By using nodes like &lt;code&gt;991536a&lt;/code&gt;, &lt;code&gt;8eb735a&lt;/code&gt;, &lt;code&gt;1a5604e&lt;/code&gt;, and &lt;code&gt;04dccb9&lt;/code&gt;, we ground the concept of &amp;ldquo;process contracturization&amp;rdquo; in actual Git evolution.&lt;/li&gt;
&lt;li&gt;The series pattern is reserved for this article to illustrate that blog writing has moved from generating single pieces to managing entire sets of deliverables.&lt;/li&gt;
&lt;li&gt;The ending deliberately points toward the token wall, setting up the groundwork for data engineering and preprocessing in the second article.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Skill is not a new prompt, it is the job manual for the agent.</title>
        <link>https://ttf248.life/en/p/skill-is-an-agent-handbook/</link>
        <pubDate>Thu, 02 Apr 2026 22:43:16 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/skill-is-an-agent-handbook/</guid>
        <description>&lt;p&gt;These past few days, while reading about AI programming, people were first discussing &lt;code&gt;MCP&lt;/code&gt;, and then immediately started talking about &lt;code&gt;Skill&lt;/code&gt;. Many people who see this term for the first time will instinctively treat it as another new protocol or another advanced prompt.&lt;/p&gt;
&lt;p&gt;My judgment is very straightforward: &lt;code&gt;Skill&lt;/code&gt; isn&amp;rsquo;t here to replace &lt;code&gt;MCP&lt;/code&gt;; rather, it&amp;rsquo;s more like providing an occupational manual for the agent. &lt;code&gt;MCP&lt;/code&gt; solves the problem of &amp;ldquo;enabling the agent to connect to the external world,&amp;rdquo; while &lt;code&gt;Skill&lt;/code&gt; solves the problem of &amp;ldquo;how to reliably get the job done after connecting.&amp;rdquo; These two are not a replacement relationship; they are more like one following the other.&lt;/p&gt;
&lt;p&gt;Simply put, &lt;code&gt;MCP&lt;/code&gt; gives the agent hands and feet, and &lt;code&gt;Skill&lt;/code&gt; tells the agent not to mess around.&lt;/p&gt;
&lt;h2 id=&#34;what-exactly-is-a-skill&#34;&gt;What exactly is a Skill?
&lt;/h2&gt;&lt;p&gt;If I were to explain it in the simplest terms, I would say this:
It&amp;rsquo;s like taking the expert knowledge from a seasoned employee&amp;rsquo;s head and organizing it into a reusable, triggerable, and actionable manual. That thing is a &lt;code&gt;Skill&lt;/code&gt;.
The official OpenAI documentation defines it very directly: A &lt;code&gt;Skill&lt;/code&gt; is a package of capabilities designed for specific tasks. It can contain instructions, reference materials, and optional scripts. The goal isn&amp;rsquo;t to make the model &amp;ldquo;smarter,&amp;rdquo; but rather to ensure that when performing a certain type of task, it outputs results consistently according to a fixed workflow.
What is it most like?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s not like a regular prompt because it&amp;rsquo;s not something you just say once and are done with.&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s not like an &lt;code&gt;MCP&lt;/code&gt; because it doesn&amp;rsquo;t handle connecting tools and data sources.&lt;/li&gt;
&lt;li&gt;And it&amp;rsquo;s not like the general rules in &lt;code&gt;AGENTS.md&lt;/code&gt; because it&amp;rsquo;s not a universal rule for the entire repository.
It is more like a specialized Standard Operating Procedure (SOP), or a trade manual.
For example:&lt;/li&gt;
&lt;li&gt;When handling GitHub PR comments, first identify which comments need attention, then ask the user which ones to handle, then modify the code, and finally provide feedback.&lt;/li&gt;
&lt;li&gt;When debugging a CI failure, first pull the GitHub Actions logs, then extract the failing segments, then propose a fix plan, and only execute after approval.&lt;/li&gt;
&lt;li&gt;When writing a blog post, first generate titles according to a fixed style, then supplement with facts, then add frontmatter, and finally publish it.
The common thread among these tasks is not that &amp;ldquo;the model doesn&amp;rsquo;t know how to answer,&amp;rdquo; but rather that &amp;ldquo;the model&amp;rsquo;s approach varies every time, making it easy for it to drift off course.&amp;rdquo; This is where the value of a &lt;code&gt;Skill&lt;/code&gt; comes into play.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-difference-between-skill-and-mcp&#34;&gt;The Difference Between Skill and MCP
&lt;/h2&gt;&lt;p&gt;This issue really needs to be viewed within a real workflow, otherwise, it&amp;rsquo;s easy to talk too theoretically.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;MCP&lt;/code&gt; is like the interface layer.
The official definition of &lt;code&gt;MCP&lt;/code&gt; is an open standard for connecting AI applications to external systems. Files, local databases, search engines, design mockups, third-party services—all of these can be connected via &lt;code&gt;MCP&lt;/code&gt;. Therefore, it solves the problem of &amp;ldquo;connecting things.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Skill&lt;/code&gt; is like the process layer.
The OpenAI &lt;code&gt;Agent Skills&lt;/code&gt; documentation makes it very clear that a &lt;code&gt;Skill&lt;/code&gt; is the writing format for reusable workflows. A &lt;code&gt;Skill&lt;/code&gt; must have at least an &lt;code&gt;SKILL.md&lt;/code&gt;, and can also include &lt;code&gt;scripts/&lt;/code&gt;, &lt;code&gt;references/&lt;/code&gt;, and &lt;code&gt;assets/&lt;/code&gt;. Codex first reads its &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt;; only when it determines that it needs to use it does it load the full description into the context. This is what the official documentation calls &lt;code&gt;progressive disclosure&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, the division of labor between the two is very clear:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MCP&lt;/code&gt;: Connects the capabilities (the &amp;ldquo;what&amp;rdquo;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Skill&lt;/code&gt;: Defines the order of operations (the &amp;ldquo;how&amp;rdquo;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To give a very intuitive example, something like converting Figma to code. If the agent cannot read the design mockup at all, then what you need first is an &lt;code&gt;MCP&lt;/code&gt;. But if it can already read the design mockup, but just writes things randomly—writing components today, cutting pages tomorrow, and forgetting visual checks the day after—then what you need to improve is the &lt;code&gt;Skill&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;how-to-develop-skills&#34;&gt;How to Develop Skills
&lt;/h2&gt;&lt;p&gt;This is actually not as heavy as you might think.
OpenAI officially recommends starting with the built-in &lt;code&gt;$skill-creator&lt;/code&gt;, which will help you build the basic structure, such as trigger conditions, scope, and whether a script is needed. By default, it prioritizes instruction-only, meaning don&amp;rsquo;t rush to write scripts; first, make sure your instructions are clear.
If writing manually, the minimum structure is very simple:&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;Of these, only &lt;code&gt;SKILL.md&lt;/code&gt; is truly essential. Furthermore, this file must have at least two pieces of metadata:&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;I think the most crucial steps when developing a &lt;code&gt;Skill&lt;/code&gt; are the following.&lt;/p&gt;
&lt;h3 id=&#34;1-look-for-recurring-biases&#34;&gt;1. Look for &amp;ldquo;Recurring Biases&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;Not every task is worth turning into a &lt;code&gt;Skill&lt;/code&gt;.
If you only ask the agent to do something occasionally, a regular prompt is enough. The situations that are truly suitable for creating a &lt;code&gt;Skill&lt;/code&gt; are usually like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have repeated the same thing many times.&lt;/li&gt;
&lt;li&gt;The agent has the capability, but the execution order always changes.&lt;/li&gt;
&lt;li&gt;The location where it makes mistakes is similar every time.
Simply put, it&amp;rsquo;s not an &amp;ldquo;ability gap,&amp;rdquo; but rather an &amp;ldquo;inconsistent process.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-write-the-description-as-a-trigger-condition-not-as-marketing-copy&#34;&gt;2. Write the &lt;code&gt;description&lt;/code&gt; as a trigger condition, not as marketing copy
&lt;/h3&gt;&lt;p&gt;This step is crucial.
The official documentation specifically emphasizes that whether Codex will implicitly call a &lt;code&gt;Skill&lt;/code&gt; heavily depends on the &lt;code&gt;description&lt;/code&gt;. So, don&amp;rsquo;t write things like &amp;ldquo;This is a very useful skill&amp;rdquo;; instead, write &amp;ldquo;When it should be used and when it should &lt;em&gt;not&lt;/em&gt; be used.&amp;rdquo;
For example, the description for the official &lt;code&gt;gh-fix-ci&lt;/code&gt; skill is very clear: Use this when the user asks you to debug or fix failed GitHub PR checks; the focus is on checking logs, summarizing the failure reasons, providing a remediation plan, and only implementing it after receiving explicit approval. Just by reading it, you know its boundaries.&lt;/p&gt;
&lt;h3 id=&#34;3-if-it-can-be-solved-with-instructions-dont-write-a-script-first&#34;&gt;3. If it can be solved with instructions, don&amp;rsquo;t write a script first
&lt;/h3&gt;&lt;p&gt;OpenAI&amp;rsquo;s documentation is also very practical: unless you explicitly need deterministic behavior or external tools, prioritize using instructions rather than scripting everything right away.
Why? Because the more scripts you have, the higher the maintenance cost becomes.
Many &lt;code&gt;Skills&lt;/code&gt; initially only require clearly explaining the steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What to do first&lt;/li&gt;
&lt;li&gt;What to do next&lt;/li&gt;
&lt;li&gt;What the output format should be&lt;/li&gt;
&lt;li&gt;In which situations to stop and ask the user
This can solve most of the problems.
Only when a step is particularly stable, mechanical, or highly suitable for automation should you move it down into &lt;code&gt;scripts/&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;4-extract-data-templates-and-resources&#34;&gt;4. Extract Data, Templates, and Resources
&lt;/h3&gt;&lt;p&gt;As you write the &lt;code&gt;Skill&lt;/code&gt;, it&amp;rsquo;s easy for it to become one long block of descriptive text. This is when you need to break things out.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SKILL.md&lt;/code&gt; is responsible for explaining rules and sequence.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;references/&lt;/code&gt; holds background documents and reference materials.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assets/&lt;/code&gt; stores templates, icons, and examples.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scripts/&lt;/code&gt; contains actions that can be executed reliably.
Doing this has two benefits. First, the main file won&amp;rsquo;t become increasingly bloated. Second, the model only loads details when necessary, which aligns with the concept of &lt;em&gt;progressive disclosure&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;5-local-use-first-cross-project-distribution-later&#34;&gt;5. Local Use First, Cross-Project Distribution Later
&lt;/h3&gt;&lt;p&gt;The official documentation explains this very clearly.
If you are only using it for your current repository, placing it in &lt;code&gt;.agents/skills/&lt;/code&gt; is sufficient. Codex scans the skills directory from locations such as the repository, user, and system.
However, if you find that this functionality can be used across more than one repository, or if you want to package and distribute multiple skills together, then don&amp;rsquo;t stop at just the &lt;code&gt;skill&lt;/code&gt; folder level; you should consider using &lt;code&gt;plugin&lt;/code&gt;. The official OpenAI documentation is also very clear: &lt;code&gt;Skill&lt;/code&gt; is the workflow itself, while &lt;code&gt;plugin&lt;/code&gt; is the unit that is better suited for installation and distribution.&lt;/p&gt;
&lt;h2 id=&#34;what-scenarios-are-suitable-for-skills&#34;&gt;What Scenarios are Suitable for Skills
&lt;/h2&gt;&lt;p&gt;Having more &lt;code&gt;Skills&lt;/code&gt; is not always better; they are best suited for the following types of tasks.&lt;/p&gt;
&lt;h3 id=&#34;high-frequency-repetitive-tasks&#34;&gt;High-Frequency Repetitive Tasks
&lt;/h3&gt;&lt;p&gt;Tasks you do every week, and the sequence is pretty much the same each time.
For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Handling PR review comments&lt;/li&gt;
&lt;li&gt;Writing a blog post and adding frontmatter&lt;/li&gt;
&lt;li&gt;Debugging CI failures&lt;/li&gt;
&lt;li&gt;Performing pre-release checks
What&amp;rsquo;s most daunting about these kinds of tasks isn&amp;rsquo;t that the model can&amp;rsquo;t do it, but having to explain it all over again every single time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;tasks-with-fixed-procedures&#34;&gt;Tasks with Fixed Procedures
&lt;/h3&gt;&lt;p&gt;Some tasks naturally have a sequence.
For example, when troubleshooting an issue, you should first check the logs, then narrow down the scope, then propose a plan, and finally make changes. In this case, &lt;code&gt;Skill&lt;/code&gt; is particularly suitable because it can enforce the order, reducing reliance on the model&amp;rsquo;s ad-hoc performance.&lt;/p&gt;
&lt;h3 id=&#34;tasks-requiring-domain-context-binding&#34;&gt;Tasks Requiring Domain Context Binding
&lt;/h3&gt;&lt;p&gt;Some tasks do not have fixed steps and come with strong constraints.
For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Must only check official documentation&lt;/li&gt;
&lt;li&gt;Must output in a specific review format&lt;/li&gt;
&lt;li&gt;Must retain existing terminology within the team&lt;/li&gt;
&lt;li&gt;Must comply with the writing or development standards of a certain repository
If you rely on prompts alone every time, it&amp;rsquo;s easy to miss these details.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;tasks-requiring-tools-and-processes-together&#34;&gt;Tasks Requiring Tools and Processes Together
&lt;/h3&gt;&lt;p&gt;This scenario is particularly typical.
It&amp;rsquo;s not just about &amp;ldquo;connecting to GitHub&amp;rdquo; and being done; after connecting, you still need to examine the logs in a certain way, deduce the problems, decide whether or not to modify anything, and finally how to provide feedback. In other words, external connections and internal processes must work together.
This is often when &lt;code&gt;MCP + Skill&lt;/code&gt; appear jointly.&lt;/p&gt;
&lt;h2 id=&#34;when-not-to-use-a-skill&#34;&gt;When Not to Use a Skill
&lt;/h2&gt;&lt;p&gt;It&amp;rsquo;s also important to be clear about this, otherwise it&amp;rsquo;s easy to want to make everything into a &lt;code&gt;Skill&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;one-off-casual-tasks&#34;&gt;One-off Casual Tasks
&lt;/h3&gt;&lt;p&gt;When a user asks a quick question, a standard prompt is often sufficient.&lt;/p&gt;
&lt;h3 id=&#34;you-just-want-to-connect-an-external-system&#34;&gt;You just want to &amp;ldquo;connect an external system&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;In that case, prioritize &lt;code&gt;MCP&lt;/code&gt;, not &lt;code&gt;Skill&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;you-want-to-constrain-the-long-term-behavior-of-the-entire-repository&#34;&gt;You want to constrain the long-term behavior of the entire repository
&lt;/h3&gt;&lt;p&gt;This is more like the job of &lt;code&gt;AGENTS.md&lt;/code&gt;, not a &lt;code&gt;Skill&lt;/code&gt;.
So, to summarize simply:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Missing connections, use &lt;code&gt;MCP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Missing processes/workflows, use &lt;code&gt;Skill&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Missing global rules, use &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;several-representative-examples&#34;&gt;Several Representative Examples
&lt;/h2&gt;&lt;p&gt;It&amp;rsquo;s better to look at a few real-world examples than to hear too many concepts.&lt;/p&gt;
&lt;h3 id=&#34;1-roll-dice-the-minimal-viable-entry-case&#34;&gt;1. &lt;code&gt;roll-dice&lt;/code&gt;: The Minimal Viable Entry Case
&lt;/h3&gt;&lt;p&gt;This example comes from the official OpenAI &lt;code&gt;Agent Skills&lt;/code&gt; documentation.
It is very small; there is almost only an &lt;code&gt;SKILL.md&lt;/code&gt; in the directory, which allows the agent to call PowerShell&amp;rsquo;s random number command when the user requests rolling dice.
Why is this example good?
Because it directly exposes the most core skeleton of a &lt;code&gt;Skill&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It has clear triggering conditions&lt;/li&gt;
&lt;li&gt;It has a clear execution method&lt;/li&gt;
&lt;li&gt;It has clear boundaries
It illustrates one thing: a &lt;code&gt;Skill&lt;/code&gt; doesn&amp;rsquo;t have to be large. As long as something happens repeatedly, and you don&amp;rsquo;t want the model to improvise wildly, you can create it as a &lt;code&gt;Skill&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-gh-address-comments-a-workflow-example-for-handling-github-comments&#34;&gt;2. &lt;code&gt;gh-address-comments&lt;/code&gt;: A Workflow Example for Handling GitHub Comments
&lt;/h3&gt;&lt;p&gt;This example comes from the official 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; repository.
Its goal is not to &amp;ldquo;connect to GitHub,&amp;rdquo; but rather to encapsulate the process of &amp;ldquo;handling comments on the current branch&amp;rsquo;s PR.&amp;rdquo; The steps in the official version are very typical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, confirm if &lt;code&gt;gh&lt;/code&gt; is already authenticated.&lt;/li&gt;
&lt;li&gt;Then, fetch the comments and review threads for the current PR.&lt;/li&gt;
&lt;li&gt;Number and summarize these comments.&lt;/li&gt;
&lt;li&gt;Allow the user to explicitly select which ones need processing.&lt;/li&gt;
&lt;li&gt;Only then start the actual work.
This example particularly illustrates the value of a &lt;code&gt;Skill&lt;/code&gt;.
For many engineering tasks, the difficulty isn&amp;rsquo;t whether &amp;ldquo;the model knows what GitHub is,&amp;rdquo; but rather &amp;ldquo;whether it will process things in the correct order.&amp;rdquo; &lt;code&gt;gh-address-comments&lt;/code&gt; solves exactly this kind of sequencing problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-gh-fix-ci-troubleshooting-engineering-cases-with-failed-ci&#34;&gt;3. &lt;code&gt;gh-fix-ci&lt;/code&gt;: Troubleshooting Engineering Cases with Failed CI
&lt;/h3&gt;&lt;p&gt;This is also the official skill in the &lt;code&gt;openai/skills&lt;/code&gt; repository.
It addresses another very typical engineering task: a PR check has failed, should it be fixed, and how to fix it.
The workflow defined in this &lt;code&gt;Skill&lt;/code&gt; is also very representative:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, confirm the &lt;code&gt;gh&lt;/code&gt; login status.&lt;/li&gt;
&lt;li&gt;Find the current PR.&lt;/li&gt;
&lt;li&gt;Pull the failed checks and logs from GitHub Actions.&lt;/li&gt;
&lt;li&gt;Extract the failure snippets.&lt;/li&gt;
&lt;li&gt;Propose a fix plan first.&lt;/li&gt;
&lt;li&gt;Only act after getting approval.
This scenario cannot be reliably handled by a simple prompt like &amp;ldquo;Help me see why CI failed.&amp;rdquo; Because it involves permissions, logs, external tools, approval boundaries, and execution order—all of which need to be defined.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;4-private-repository-skills-solidifying-teams-own-methodologies&#34;&gt;4. Private Repository Skills: Solidifying Team&amp;rsquo;s Own Methodologies
&lt;/h3&gt;&lt;p&gt;Beyond the official examples, I think the greater value of &lt;code&gt;Skill&lt;/code&gt; lies within private repositories.
For instance, the &lt;code&gt;blog-writer&lt;/code&gt; in this blog repository is essentially a very typical repo-scoped skill. It doesn&amp;rsquo;t aim to &amp;ldquo;teach the model how to write Chinese,&amp;rdquo; but rather codifies the writing style, structure, fact-checking process, output path, and final storage format that have already been established within this specific repository into a workflow.
These kinds of &lt;code&gt;Skills&lt;/code&gt; often hold the most practical value. This is because they are not designed for everyone; instead, they specifically solve the problem: &amp;ldquo;In this repository, what kind of task keeps recurring, and where do we tend to go off track?&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;when-to-actually-use-a-skill&#34;&gt;When to Actually Use a Skill
&lt;/h2&gt;&lt;p&gt;So, let&amp;rsquo;s get back to the most practical question: when should you seriously implement a &lt;code&gt;Skill&lt;/code&gt;?
My answer is:
When you realize the problem is no longer &amp;ldquo;the model lacks capability,&amp;rdquo; but rather &amp;ldquo;its execution is inconsistent every time.&amp;rdquo;
At this point, continuously stacking prompts yields diminishing returns. If you add one sentence today and another tomorrow, eventually the prompt becomes like a rambling diary entry, and the model will still make mistakes.
Instead, organizing it into a &lt;code&gt;Skill&lt;/code&gt;—separating the trigger conditions, steps, boundaries, scripts, and necessary data—results in more stable and reusable outcomes.
The &lt;code&gt;MCP&lt;/code&gt; brings in the external world; the &lt;code&gt;Skill&lt;/code&gt; solidifies the internal methodology.
The former solves &amp;ldquo;if it can be done,&amp;rdquo; while the latter solves &amp;ldquo;how to do it reliably.&amp;rdquo;
This is my current understanding of a &lt;code&gt;Skill&lt;/code&gt;.
It&amp;rsquo;s not just a new prompt, nor is it a new protocol.
It&amp;rsquo;s more like an operational manual for the agent.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Prompt: AI Large Model Programming, first appeared MCP, then Skill. Using plain language, explain what Skill is, how to develop a Skill, what scenarios are suitable for Skill, and provide specific, representative examples from each source.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-approach-summary&#34;&gt;Writing Approach Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Instead of repeating a &amp;ldquo;concept overview of &lt;code&gt;MCP&lt;/code&gt; and &lt;code&gt;Skill&lt;/code&gt;,&amp;rdquo; the focus is placed on the role and boundaries of &lt;code&gt;Skill&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Abstract definitions are grounded in real workflows using common analogies like &amp;ldquo;trade manuals&amp;rdquo; or &amp;ldquo;specialized SOPs.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;The development section follows the actual structure of official documentation, retaining key points such as &lt;code&gt;description&lt;/code&gt;, &lt;code&gt;progressive disclosure&lt;/code&gt;, and &lt;code&gt;instruction-only&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Case studies prioritize official sources, utilizing &lt;code&gt;roll-dice&lt;/code&gt; from the official documentation, along with &lt;code&gt;gh-address-comments&lt;/code&gt; and &lt;code&gt;gh-fix-ci&lt;/code&gt; from the &lt;code&gt;openai/skills&lt;/code&gt; repository.&lt;/li&gt;
&lt;li&gt;The conclusion re-clarifies the boundaries between &lt;code&gt;MCP&lt;/code&gt;, &lt;code&gt;Skill&lt;/code&gt;, and &lt;code&gt;AGENTS.md&lt;/code&gt; to prevent readers from remaining confused after reading.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Don&#39;t force weak models onto hard tasks.</title>
        <link>https://ttf248.life/en/p/weaker-models-shouldnt-do-frontier-work/</link>
        <pubDate>Thu, 02 Apr 2026 22:05:00 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/weaker-models-shouldnt-do-frontier-work/</guid>
        <description>&lt;p&gt;Recently, I&amp;rsquo;ve been migrating some edge cases to &lt;code&gt;MiniMax&lt;/code&gt; and local models. The more I use them, the more I feel that we shouldn&amp;rsquo;t always measure things by the standard of &amp;ldquo;the most powerful model.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;My judgment is straightforward: don&amp;rsquo;t force weak models into hard tasks. Models like &lt;code&gt;MiniMax&lt;/code&gt; are indeed limited in capability, but for complex coding, long-chain reasoning, or ambiguous requirement decomposition, they fall a bit short. However, if you ask it to do data cleaning, document writing, or searching for proposal materials—these kinds of tasks—it can handle them perfectly well. The same logic applies to local models around the &lt;code&gt;12B&lt;/code&gt; size; translation, format rewriting, and batch cleaning are actually where they are best suited.&lt;/p&gt;
&lt;p&gt;To put it plainly, it&amp;rsquo;s not that the models lack value; it&amp;rsquo;s just that we shouldn&amp;rsquo;t place them in the wrong roles.&lt;/p&gt;
&lt;h2 id=&#34;the-real-problem-isnt-how-strong-the-model-is-but-whether-it-works-correctly&#34;&gt;The real problem isn&amp;rsquo;t how strong the model is, but whether it works correctly.
&lt;/h2&gt;&lt;p&gt;Many people who talk about large models automatically think of the most difficult tasks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Writing complex engineering code independently&lt;/li&gt;
&lt;li&gt;Deconstructing an entire system in one go&lt;/li&gt;
&lt;li&gt;Multi-turn reasoning over long contexts&lt;/li&gt;
&lt;li&gt;Planning and executing while searching
These are certainly important. But in real-world work, what is actually piled on your desk most often isn&amp;rsquo;t these kinds of tasks.
It&amp;rsquo;s more like:&lt;/li&gt;
&lt;li&gt;Cleaning up a pile of dirty fields&lt;/li&gt;
&lt;li&gt;Organizing scattered information into readable documents&lt;/li&gt;
&lt;li&gt;Converting long texts into summaries, FAQs, or outlines&lt;/li&gt;
&lt;li&gt;Standardizing mixed Chinese and English content formats&lt;/li&gt;
&lt;li&gt;Gathering data from multiple web pages and then compiling it into a draft proposal
For these types of tasks, what is most needed is not &amp;ldquo;the model thinking like a genius,&amp;rdquo; but three things:&lt;/li&gt;
&lt;li&gt;Instruction following must be reasonably accurate.&lt;/li&gt;
&lt;li&gt;Output structure should be as stable as possible.&lt;/li&gt;
&lt;li&gt;The cost must be low enough that you are willing to use it repeatedly.
This is why I always feel that weak models are not useless; they just cannot be used in the same kind of battle as flagship models.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;minimax-whats-actually-suitable-for-it&#34;&gt;MiniMax: What&amp;rsquo;s Actually Suitable for It
&lt;/h2&gt;&lt;p&gt;First, let&amp;rsquo;s talk about &lt;code&gt;MiniMax&lt;/code&gt;.
The official positioning of &lt;code&gt;MiniMax-M2.5&lt;/code&gt; is actually quite high. In press releases and open platform documentation, they push it towards scenarios like programming, tool calling, search, and office productivity, even emphasizing speed and cost advantages. I don&amp;rsquo;t completely disbelieve these claims, but I prefer to break them down.
For me, what &lt;code&gt;MiniMax&lt;/code&gt; is genuinely good at isn&amp;rsquo;t &amp;ldquo;the most complex development tasks,&amp;rdquo; but rather the following:&lt;/p&gt;
&lt;h3 id=&#34;data-cleaning&#34;&gt;Data Cleaning
&lt;/h3&gt;&lt;p&gt;A lot of data cleaning is essentially manual labor involving semi-structured text.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name unification&lt;/li&gt;
&lt;li&gt;Field mapping&lt;/li&gt;
&lt;li&gt;Anomaly labeling&lt;/li&gt;
&lt;li&gt;Classification tagging&lt;/li&gt;
&lt;li&gt;Table field completion
What these types of tasks fear most is not the model being &amp;ldquo;dumb,&amp;rdquo; but rather inconsistent formatting or divergent outputs. As long as the model can reliably output results in &lt;code&gt;JSON&lt;/code&gt;, tables, or fixed templates, it&amp;rsquo;s actually sufficient. While powerful models certainly can do this, using the most expensive tier of model just to clean fields is often not cost-effective.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;documentation-writing&#34;&gt;Documentation Writing
&lt;/h3&gt;&lt;p&gt;Writing documentation is annoying, not difficult.
When an interface changes, a process changes, or a field is modified, the documentation has to change accordingly. This process doesn&amp;rsquo;t actually require the model to have strong creativity; rather, it requires it &lt;em&gt;not&lt;/em&gt; to over-exert itself and alter clearly defined things into something ambiguous.
&lt;code&gt;MiniMax&lt;/code&gt; is often more reliable for these kinds of tasks than one might expect. Especially when you have already prepared the context, it acts more like a capable documentation assistant rather than an actual engineer.&lt;/p&gt;
&lt;h3 id=&#34;solution-material-search&#34;&gt;Solution Material Search
&lt;/h3&gt;&lt;p&gt;The official platform is also promoting search and tool calling, so this direction is fine.
Many times, what we need is not for the model to &amp;ldquo;come up with an answer out of thin air,&amp;rdquo; but rather for it to first find relevant web pages, documents, announcements, or materials, and then organize them neatly. In this scenario, cheaper models like &lt;code&gt;MiniMax&lt;/code&gt; are very valuable because searching, summarizing, and integrating are inherently high-frequency, mundane tasks.
So my actual view is: &lt;code&gt;MiniMax&lt;/code&gt; isn&amp;rsquo;t incapable; rather, it is better suited for the dirty, tiring, and repetitive tasks within a production pipeline. If you let it act as an assistant or general laborer, it is often competent; but if you ask it to handle the entire engineering process, the probability of disappointment increases.&lt;/p&gt;
&lt;h2 id=&#34;local-12b-models-best-suited-for-bringing-back-these-tasks&#34;&gt;Local 12B Models, Best Suited for Bringing Back These Tasks
&lt;/h2&gt;&lt;p&gt;Looking further down, the logic for local deployment is actually the same.
When many people talk about local models, they inevitably ask one question: Can it replace the flagship cloud models?
I think this question is flawed from the start.
For local models around &lt;code&gt;12B&lt;/code&gt;, what has real practical value isn&amp;rsquo;t &amp;ldquo;proving that it can handle the most powerful tasks,&amp;rdquo; but rather bringing back those stable, repetitive, sensitive, low-profit, yet high-frequency tasks.&lt;/p&gt;
&lt;h3 id=&#34;translation&#34;&gt;Translation
&lt;/h3&gt;&lt;p&gt;This is one of the most natural scenarios for local models.
As explicitly mentioned in the official blog of &lt;code&gt;Qwen2.5&lt;/code&gt;, it has enhanced capabilities for long-text generation, structured data understanding, and &lt;code&gt;JSON&lt;/code&gt; output, and supports over 29 languages. This combination is inherently suitable for tasks like translation, bilingual rewriting, format standardization, and terminology normalization.
Technical documentation, field descriptions, product introductions, and API comments—these items often have stable structures and fixed terminology. While local models might not produce the most elegant translations, they are usually sufficient.&lt;/p&gt;
&lt;h3 id=&#34;data-cleaning-1&#34;&gt;Data Cleaning
&lt;/h3&gt;&lt;p&gt;This is also where local models are particularly realistic.
Many spreadsheets, documents, and business materials that you might not want to upload to the cloud. Especially internal data, customer records, meeting minutes, and draft proposals—when privacy and permissions are involved, running it locally provides much more peace of mind.
At this point, the significance of a local model around &lt;code&gt;12B&lt;/code&gt; isn&amp;rsquo;t &amp;ldquo;how smart it is,&amp;rdquo; but rather that &amp;ldquo;it&amp;rsquo;s on my machine, and it can reliably handle these dirty tasks.&amp;rdquo;&lt;/p&gt;
&lt;h3 id=&#34;fixed-format-rewriting&#34;&gt;Fixed Format Rewriting
&lt;/h3&gt;&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Meeting minutes organized into a fixed template&lt;/li&gt;
&lt;li&gt;Product titles cleaned into a unified naming convention&lt;/li&gt;
&lt;li&gt;Bug descriptions rewritten into ticket format&lt;/li&gt;
&lt;li&gt;Mixed Chinese and English text cleaned into single-language versions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These types of tasks share consistent characteristics: clear rules, large batches, high repetition, low value per instance, but significant cumulative effort.
This is exactly what local models are best suited for.&lt;/p&gt;
&lt;h2 id=&#34;can-the-3060-12gb-actually-run-a-model-around-12b&#34;&gt;Can the 3060 12GB actually run a model around 12B?
&lt;/h2&gt;&lt;p&gt;I prefer to write about this realistically: &amp;ldquo;It can run it, but don&amp;rsquo;t get your hopes up too high.&amp;rdquo;
Google provided a very useful VRAM table in the official documentation for &lt;code&gt;Gemma 3&lt;/code&gt;. The &lt;code&gt;Gemma 3 12B&lt;/code&gt; roughly requires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;About &lt;code&gt;20 GB&lt;/code&gt; of VRAM to load the full precision version.&lt;/li&gt;
&lt;li&gt;About &lt;code&gt;12.2 GB&lt;/code&gt; to load the medium quantization version.&lt;/li&gt;
&lt;li&gt;About &lt;code&gt;8.7 GB&lt;/code&gt; to load a lower VRAM consumption version.
The official documentation also specifically reminds that this is only for model loading, and does not include prompt or runtime overhead.
This sentence is very key.
What does it mean?
It means that running a model around 12B on a card like the &lt;code&gt;3060 12GB&lt;/code&gt; is not impossible, but the prerequisites are usually:&lt;/li&gt;
&lt;li&gt;You are running a quantized version.&lt;/li&gt;
&lt;li&gt;The context length should not be too long.&lt;/li&gt;
&lt;li&gt;The task shouldn&amp;rsquo;t be too complex.&lt;/li&gt;
&lt;li&gt;You accept average, or even slow, speed.
If you are willing to accept these premises, then running a local 12B model is indeed feasible. Tasks like translation, summarization, table cleaning, and fixed format conversion are not exaggerated in this regard.
Furthermore, the official repository for &lt;code&gt;Qwen2.5-14B-Instruct-GGUF&lt;/code&gt; itself provides multiple quantization formats, which actually makes the intention very clear: models in this category are inherently adapted for the local inference ecosystem.
So my conclusion has never been that &amp;ldquo;the 3060 12GB can easily handle a 12B model,&amp;rdquo; but rather:
It can run these types of models, but it is better suited for work with low expectations, high repetition, and high privacy requirements.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;cheap-models-and-local-models-its-not-just-about-saving-api-costs&#34;&gt;Cheap Models and Local Models: It&amp;rsquo;s Not Just About Saving API Costs
&lt;/h2&gt;&lt;p&gt;When people talk about this, the first reaction is always saving money.
Of course, saving money is important. But I think the greater value is that you start daring to outsource all those little tasks you used to avoid doing.
Before, you might not have written a dedicated script just to clean up a few hundred data points. You also wouldn&amp;rsquo;t manually adjust dozens of pages of mixed Chinese and English documents to achieve uniform formatting. And you certainly wouldn&amp;rsquo;t read through every single webpage to gather materials for an ad-hoc proposal.
Things are different now.
As long as the cost is low enough and the barrier is low enough, these tasks that were previously considered &amp;ldquo;not worth the effort&amp;rdquo; suddenly become worthwhile. You no longer hesitate over whether or not to do it; instead, you just throw it to a cheap model or a local model to run through first.
This is what I see as the most realistic change.
Powerful models are responsible for tackling core problems, weaker models handle miscellaneous tasks, and local models provide fallback and batch processing.
With this division of labor, the entire workflow becomes smooth.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;So, the final word remains: don&amp;rsquo;t always try to make one model conquer everything.
Models like &lt;code&gt;MiniMax&lt;/code&gt; are weak in capability, but they aren&amp;rsquo;t useless. If you use them to tackle complex engineering tasks, vague requirements, or multi-turn reasoning, you will naturally be disappointed; however, if you use them for data cleaning, document drafting, or searching for proposal materials, they often work quite smoothly.
The same applies to local models around &lt;code&gt;12B&lt;/code&gt;. Their purpose isn&amp;rsquo;t to prove that &amp;ldquo;I no longer need cloud flagships,&amp;rdquo; but rather to reliably move stable, repetitive, sensitive, and high-volume tasks back onto their own machines.
Simply put: don&amp;rsquo;t let a weak model do what it is not good at.
Place them in the right role, and they will have real value.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&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 Open Platform: Text Generation&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;h2 id=&#34;writing-notes&#34;&gt;Writing Notes
&lt;/h2&gt;&lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;Minimax&amp;rsquo;s large model is weak in capability, but it&amp;rsquo;s fine for tasks like data cleaning, document writing, and searching for proposal materials; with the same logic, deploying a large model locally for translation or data cleaning work is also good. The model parameter count is around 12b, and even a local GPU like the RTX 3060 with 12GB can handle it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;writing-outline-summary&#34;&gt;Writing Outline Summary
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Retained the core judgment of &amp;ldquo;don&amp;rsquo;t force weak models onto hard tasks,&amp;rdquo; and did not write it as a model leaderboard comparison.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;MiniMax&lt;/code&gt; section is mainly based on the official positioning for programming, searching, and office work, then applies this judgment back to real-world tasks like data cleaning, document handling, and information retrieval.&lt;/li&gt;
&lt;li&gt;For local models, I selected two officially sourced options: &lt;code&gt;Qwen2.5&lt;/code&gt; and &lt;code&gt;Gemma 3&lt;/code&gt;, one supporting multilingual and structured output, and the other supporting &lt;code&gt;12B&lt;/code&gt; size and VRAM usage.&lt;/li&gt;
&lt;li&gt;The description for the &lt;code&gt;3060 12GB&lt;/code&gt; was intentionally phrased as &amp;ldquo;capable, but don&amp;rsquo;t get too carried away,&amp;rdquo; to avoid presenting quantized inference as an absolute conclusion.&lt;/li&gt;
&lt;li&gt;In the conclusion, I re-categorized strong models, weak models, and local models based on their respective roles, making the main thread more focused.&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>The End of Low-Cost API Gateways: Large Model Experiences and the Impossible Triangle in March</title>
        <link>https://ttf248.life/en/p/the-end-of-low-cost-api-relays/</link>
        <pubDate>Mon, 30 Mar 2026 20:00:00 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/the-end-of-low-cost-api-relays/</guid>
        <description>&lt;p&gt;Throughout March, I was constantly testing between various large model API hubs.
It is indeed cheap. You can test out foreign models like ChatGPT, Claude, and Gemini for a small amount of money per month, which at first glance seems like finding an extremely cost-effective solution. However, after actually using it, I increasingly feel that this path has always been constrained by an impossible triangle: &lt;strong&gt;Quality, Stability, and Affordability—it is difficult for all three to be achieved simultaneously.&lt;/strong&gt;
By last weekend, the situation became quite clear. During the two days from &lt;code&gt;2026-03-28&lt;/code&gt; to &lt;code&gt;2026-03-29&lt;/code&gt;, I felt a noticeable tightening of risk controls on ChatGPT channels, and Claude was no different. Many low-cost relays that were previously usable suddenly became unstable or even completely failed. For me, this basically signaled the temporary end of the low-cost API relay model.&lt;/p&gt;
&lt;h2 id=&#34;what-is-an-api-proxygateway&#34;&gt;What is an API Proxy/Gateway?
&lt;/h2&gt;&lt;p&gt;Let&amp;rsquo;s first clarify the concept.
What is called an &lt;strong&gt;API Gateway&lt;/strong&gt; (or &amp;ldquo;Proxy Station&amp;rdquo;) is not inherently a service provided by the model vendor itself, but rather an intermediary &amp;ldquo;forwarding layer&amp;rdquo; placed between the user and the upstream models. You send your request to the gateway, which then forwards it on your behalf to OpenAI, Anthropic, or other model providers, and finally returns the result to you.
From the user&amp;rsquo;s perspective, it acts like a cheaper and more &amp;ldquo;flexible&amp;rdquo; unified entry point; from a technical and business model perspective, it is more like repackaging and redistributing upstream resources.
The reason these services have been widely used is quite simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Low cost&lt;/li&gt;
&lt;li&gt;Low barrier to entry&lt;/li&gt;
&lt;li&gt;Wide variety of models available&lt;/li&gt;
&lt;li&gt;For domestic users, it saves a lot of hassle related to registration, payment, and network environment setup.
However, the problem lies precisely in this. Since it is not the official link, many conveniences are fundamentally built upon an &amp;ldquo;extra layer&amp;rdquo; rather than stable authorization.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-they-generally-work&#34;&gt;How They Generally Work
&lt;/h2&gt;&lt;p&gt;The practices vary across different platforms, but the common patterns generally fall into a few categories.&lt;/p&gt;
&lt;h3 id=&#34;1-key-secondary-distribution&#34;&gt;1. Key Secondary Distribution
&lt;/h3&gt;&lt;p&gt;Some platforms essentially take their upstream API Keys and act as a unified forwarder, then divide the quota among downstream users. What you buy is their layer of package, not a package sold directly to you by OpenAI or Anthropic.
The problem with this model is that if the upstream Key is restricted, the quota runs out, or the strategy is adjusted, the downstream experience will immediately fluctuate.&lt;/p&gt;
&lt;h3 id=&#34;2-account-pool-rotation&#34;&gt;2. Account Pool Rotation
&lt;/h3&gt;&lt;p&gt;The term &amp;ldquo;&lt;strong&gt;account pool&lt;/strong&gt;&amp;rdquo; often used in the industry can be understood as pooled management of a batch of account resources. The platform centralizes multiple accounts and calls them in rotation according to requests, which is used to distribute quota pressure and risk control pressure.
The &amp;ldquo;account pool&amp;rdquo; here is not an official term from model vendors but rather a more colloquial or jargon-heavy description. It emphasizes the resource scheduling method rather than the product capability itself. Whoever has a larger pool appears more stable in the short term; however, this stability often vanishes instantly if upstream sources begin concentrated cleanups.&lt;/p&gt;
&lt;h3 id=&#34;3-reverse-engineering-wrappers&#34;&gt;3. Reverse Engineering Wrappers
&lt;/h3&gt;&lt;p&gt;Another approach is not to use the official, standard APIs, but rather to study the request methods used by the web page or client, and then re-wrap these calling processes into an interface that &amp;ldquo;looks like an API&amp;rdquo; for users.
This &amp;ldquo;&lt;strong&gt;reverse engineering&lt;/strong&gt;&amp;rdquo; process, simply put, means not entering through the official door, but understanding how the system communicates by going through side entrances, windows, or even pipes, and then wrapping it in your own layer.
The weakness of this method is also very obvious: what works today does not guarantee that it will work tomorrow. If the page structure, authentication methods, device verification, or behavioral policies change, the entire link may fail.&lt;/p&gt;
&lt;h2 id=&#34;actual-experience-in-march&#34;&gt;Actual Experience in March
&lt;/h2&gt;&lt;p&gt;After this intensive round of testing in March, my biggest takeaway wasn&amp;rsquo;t &amp;ldquo;cheap is surprisingly good,&amp;rdquo; but rather that you must continuously endure uncertainty.
For the same requirement, one transit station might answer it well today, but tomorrow its intelligence might start declining; it might output stably in the morning but start throwing errors, timing out, or losing context in the evening. You think you are buying model capability, but what you are actually buying is a constantly fluctuating &amp;ldquo;probability service.&amp;rdquo;
If you only use it for temporary testing of the model or for some light Q&amp;amp;A, this fluctuation is bearable. But once you put it into an actual workflow, the problems become very obvious.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unstable quality, output level fluctuates wildly&lt;/li&gt;
&lt;li&gt;Insufficient stability, prone to timeouts, errors, and disconnections&lt;/li&gt;
&lt;li&gt;Poor context continuity, bad experience for long tasks&lt;/li&gt;
&lt;li&gt;Model persona and style drift after switching upstream platforms&lt;/li&gt;
&lt;li&gt;Spending time troubleshooting issues, the hidden cost is not low
In other words, the biggest problem with low-cost transit services isn&amp;rsquo;t that they &amp;ldquo;aren&amp;rsquo;t cheap,&amp;rdquo; but that they transfer much of the certainty that should have been borne by official platforms back onto the user.
On the surface, you save money, but in reality, you spend more time, mental energy, and reduce the predictability of your workflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;why-i-say-it-fell-into-the-impossible-triangle&#34;&gt;Why I Say It Fell into the Impossible Triangle
&lt;/h2&gt;&lt;p&gt;After using it repeatedly recently, I increasingly feel that this type of relay service is destined to fall into an impossible triangle.&lt;/p&gt;
&lt;h3 id=&#34;quality&#34;&gt;Quality
&lt;/h3&gt;&lt;p&gt;If you want to improve the quality, you must ensure that the upstream models are truly usable, have sufficient quotas, and experience as little throttling or degradation as possible. This task itself is not cheap.&lt;/p&gt;
&lt;h3 id=&#34;stability&#34;&gt;Stability
&lt;/h3&gt;&lt;p&gt;If you want to achieve stability, you have to handle more risk control, account loss, network fluctuations, rate limiting, and backup lines. You even need to implement more complex scheduling and fallback mechanisms yourself. These are all costs.&lt;/p&gt;
&lt;h3 id=&#34;cost-effective-or-good-value&#34;&gt;Cost-Effective (or &amp;ldquo;Good Value&amp;rdquo;)
&lt;/h3&gt;&lt;p&gt;Once you have truly solidified the first two things, the price cannot continue to be suppressed so low. The ability to maintain ultra-low prices over the long term often indicates that the underlying cost has not been genuinely solved; it has merely been deferred or spread out across a future collective failure.&lt;/p&gt;
&lt;p&gt;Therefore, many intermediaries that appear to be offering &amp;ldquo;high cost-performance&amp;rdquo; are actually maintaining a fragile balance. This balance holds up when things are calm, but once upstream risk controls tighten, this equilibrium can easily collapse.&lt;/p&gt;
&lt;h2 id=&#34;last-weekend-it-was-pretty-much-obvious&#34;&gt;Last weekend, it was pretty much obvious.
&lt;/h2&gt;&lt;p&gt;What really made me decide to give up on further tinkering was the change from &lt;code&gt;2026-03-28&lt;/code&gt; to &lt;code&gt;2026-03-29&lt;/code&gt;.
My own feeling is that the channels related to ChatGPT showed very noticeable tightening during those two days, and Claude&amp;rsquo;s risk control also strengthened in tandem. Previously, we could barely maintain a &amp;ldquo;usable&amp;rdquo; state by switching lines, models, or packages. That method suddenly no longer works.
I don&amp;rsquo;t want to make an absolute judgment here like saying &amp;ldquo;the entire industry is dead,&amp;rdquo; because someone will always say they have some other usable channel. But from the perspective of actual utility for ordinary users, this path of low-cost API relay is at least no longer worth me continuing to invest time in.
The concept of being cheap only has meaning when it&amp;rsquo;s predicated on &amp;ldquo;being able to complete the task reliably.&amp;rdquo; If even basic reliability cannot be guaranteed, low cost becomes an illusion.&lt;/p&gt;
&lt;h2 id=&#34;why-this-pattern-was-inherently-fragile&#34;&gt;Why This Pattern Was Inherently Fragile
&lt;/h2&gt;&lt;p&gt;On the surface, it might seem like the vendors are &amp;ldquo;deliberately restricting&amp;rdquo; people. However, if you look deeper, this type of pattern was built on a very fragile foundation to begin with.&lt;/p&gt;
&lt;p&gt;Vendors like OpenAI and Anthropic did not design their products based on the logic of &amp;ldquo;secondary distribution gray resale.&amp;rdquo; Their official terms already have explicit restrictions regarding the reselling of API Keys, bypassing limitations, reverse engineering, or circumventing protective measures. OpenAI&amp;rsquo;s service agreement explicitly lists &amp;ldquo;buying, selling, or transferring API Keys,&amp;rdquo; &amp;ldquo;bypassing rate limits or protective measures,&amp;rdquo; and &amp;ldquo;circumventing usage restrictions&amp;rdquo; as prohibited activities; Anthropic&amp;rsquo;s commercial terms also clearly reserve space to constrain misuse, unauthorized access, and service abuse.&lt;/p&gt;
&lt;p&gt;In other words, these relay stations are not innovating within an officially encouraged ecosystem; they are surviving in the gaps of official governance boundaries. As soon as model vendors start cleaning up seriously, this pattern will naturally be the first to take a hit.&lt;/p&gt;
&lt;p&gt;There is also a very realistic background factor: many foreign model services already have barriers related to region, payment, and account systems. Since official support regions are limited, many users are blocked out, which created the demand for relays. But just because the demand exists doesn&amp;rsquo;t mean the pattern is stable. It only indicates that there is a market for gray alternative solutions; it does not guarantee long-term certainty.&lt;/p&gt;
&lt;h2 id=&#34;final-conclusion&#34;&gt;Final Conclusion
&lt;/h2&gt;&lt;p&gt;After going through all this, my conclusion is actually simpler.
&lt;code&gt;Codex&lt;/code&gt; has a better cost-performance ratio, at least from my own usage experience; it seems more suitable for actual production environments. I haven&amp;rsquo;t heard much feedback about large-scale account bans in China, so the overall mental burden is lower.
Of course, &lt;code&gt;Codex&lt;/code&gt; isn&amp;rsquo;t without constraints. The five-hour limit and weekly limits are there, and when I use it now, I don&amp;rsquo;t act as recklessly as before—I don&amp;rsquo;t ask everything or dump everything on it to process. For many problems, I will first run them through my own mind, break them down myself, and then decide whether to spend the current quota.
Looking at it this way, limits aren&amp;rsquo;t necessarily all bad. Objectively, they force the human brain to get re-involved—they force you to organize your thoughts first, make judgments first, and prioritize, rather than outsourcing all thinking. I have written about this before: a model that is slightly &amp;ldquo;less intelligent&amp;rdquo; isn&amp;rsquo;t necessarily all bad because it forces the user to maintain a basic level of critical thinking. Looking back now, this judgment still holds true.
I am not considering &lt;code&gt;Claude&lt;/code&gt; for now. It&amp;rsquo;s not that its capabilities are weak; quite the opposite, they are still very strong. But given the uncertainty of account bans even with personal paid subscriptions, it is not suitable as my primary solution at this stage.
Regarding domestic models, I choose to continue observing. It&amp;rsquo;s not yet time for me to commit heavily or bind long-term.
So, in the end, I returned to a very simple choice: subscribing to &lt;code&gt;ChatGPT Plus&lt;/code&gt; and using it for now while continuing to watch how the large model industry evolves. Many times, the cheapest option is not necessarily the most cost-effective; the one that is the easiest to use is actually the true value proposition.&lt;/p&gt;
&lt;h2 id=&#34;reference-links&#34;&gt;Reference Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;OpenAI Supported Regions: &lt;a class=&#34;link&#34; href=&#34;https://help.openai.com/en/articles/5347006-which-countries-and-territories-are-supported-by-openai&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://help.openai.com/en/articles/5347006-which-countries-and-territories-are-supported-by-openai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;OpenAI Services Agreement: &lt;a class=&#34;link&#34; href=&#34;https://openai.com/policies/services-agreement/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://openai.com/policies/services-agreement/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Anthropic Commercial Terms: &lt;a class=&#34;link&#34; href=&#34;https://www.anthropic.com/legal/commercial-terms&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://www.anthropic.com/legal/commercial-terms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
        </item>
        <item>
        <title>Deep Dive: Memory Corruption and Cache Pollution in C&#43;&#43; with Static Lambdas</title>
        <link>https://ttf248.life/en/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/en/p/deep-dive-into-memory-corruption-and-cache-pollution-caused-by-static-lambdas-in-c/</guid>
        <description>&lt;p&gt;This article analyzes the bizarre phenomenon in C++ development where &lt;code&gt;unordered_map::find&lt;/code&gt; returns an object with mismatched fields after a hit. The root cause lies in defining a &lt;code&gt;static lambda&lt;/code&gt; within the function and using reference capture to capture local variables, leading to a dangling reference after the first call, triggering undefined behavior (UB) and polluting cache data in subsequent calls. It is recommended to address this issue by explicitly passing parameters instead of implicit capture, managing lifecycles properly, and utilizing Sanitizer tools.&lt;/p&gt;
&lt;p&gt;When building high-performance market data services or distributed caches, developers often encounter a &amp;ldquo;ghost event&amp;rdquo;: after using a Key to explicitly find an object from &lt;code&gt;std::unordered_map&lt;/code&gt;, when reading its internal fields, it turns out to be the data of another Key. This “identity misalignment” often points to an insidious C++ trap: &lt;strong&gt;conflict between &lt;code&gt;static lambda&lt;/code&gt; and lifecycle capture resulting in undefined behavior (UB).&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;failure-modes-short-lifecycles-of-references-long-lastingly-preserved&#34;&gt;Failure Modes: Short Lifecycles of References &amp;ldquo;Long-Lastingly&amp;rdquo; Preserved
&lt;/h2&gt;&lt;p&gt;When optimizing performance, some developers are accustomed to defining &lt;code&gt;static lambda&lt;/code&gt; functions within a function to reduce closure creation overhead. However, when combined with implicit capture using &lt;code&gt;[&amp;amp;]&lt;/code&gt;, this can lay a dangerous trap:&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;c1&#34;&gt;// Dangerous: static extends the lambda&amp;#39;s lifetime to process level
&lt;/span&gt;&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;// [&amp;amp;] captures a reference to the local variable current_input on the stack
&lt;/span&gt;&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_func&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 class=&#34;c1&#34;&gt;// The reference here is invalid at the second execution
&lt;/span&gt;&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_func&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;h2 id=&#34;technical-principle-analysis&#34;&gt;Technical Principle Analysis
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Lifecycle Misalignment:&lt;/strong&gt; The &lt;code&gt;static&lt;/code&gt; variable is initialized only once when the program reaches that line for the first time and persists throughout the entire process execution. This means the reference captured within &lt;code&gt;patch_func&lt;/code&gt; to &lt;code&gt;current_input&lt;/code&gt; is permanently fixed at the memory address in the stack frame &lt;strong&gt;at the time of its initial call&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dangling Reference:&lt;/strong&gt; After the first &lt;code&gt;UpdateCache&lt;/code&gt; executes, the original stack frame is destroyed. When &lt;code&gt;patch_func&lt;/code&gt; is called a second time, it continues to read and write to that now-invalidated address.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hidden Memory Corruption:&lt;/strong&gt; Due to the reuse of stack space, this address may contain new business data. Writing to it appears to be updating the value for the current Key, but in reality, it&amp;rsquo;s performing an illegal write operation on a &amp;ldquo;random&amp;rdquo; area of memory. This explains why &lt;code&gt;find&lt;/code&gt;’s Key is A, and the read Value field is B.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;solutions-and-engineering-practices&#34;&gt;Solutions and Engineering Practices
&lt;/h2&gt;&lt;h3 id=&#34;1-eliminate-implicit-capture-use-explicit-parameters&#34;&gt;1. Eliminate Implicit Capture, Use Explicit Parameters
&lt;/h3&gt;&lt;p&gt;The most reliable approach is to keep the Lambda “stateless” (Stateless), passing all dependent objects through parameters.&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;c1&#34;&gt;// Recommended practice: Stateless lambda, explicitly pass src
&lt;/span&gt;&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-avoid-using-static-to-decorate-closures&#34;&gt;2. Avoid using &lt;code&gt;static&lt;/code&gt; to decorate closures
&lt;/h3&gt;&lt;p&gt;Inside a function, unless the closure does not capture any local variables, do not use &lt;code&gt;static&lt;/code&gt;. Modern compilers are very good at optimizing non-&lt;code&gt;static&lt;/code&gt; lambdas, and the performance boost from blindly using &lt;code&gt;static&lt;/code&gt; is negligible, but it brings significant security risks.&lt;/p&gt;
&lt;h3 id=&#34;3-reinforcement-consistency-verification-and-dynamic-detection&#34;&gt;3. Reinforcement Consistency Verification and Dynamic Detection
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Assertion Checking&lt;/strong&gt;: After updating the logic, add &lt;code&gt;assert(it-&amp;gt;first == it-&amp;gt;second.symbol)&lt;/code&gt; to intercept “identity inconsistency” issues during development.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool Assistance&lt;/strong&gt;: Enable &lt;code&gt;ASan&lt;/code&gt; (AddressSanitizer) and &lt;code&gt;UBSan&lt;/code&gt; (UndefinedBehaviorSanitizer) in the test environment. These tools can accurately capture behavior of accessing invalid stack memory and immediately report errors.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;&amp;ldquo;Table lookup errors&amp;rdquo; in C++ are often just symptoms, the underlying truth is usually the failure of memory management contracts. &lt;strong&gt;Correct key lookup does not necessarily mean the value is still valid.&lt;/strong&gt; When dealing with long-lived objects (such as &lt;code&gt;static&lt;/code&gt;, global variables, and long-running callbacks), you must always be vigilant about whether the references they capture have been invalidated by stack frame destruction (“blown away”).&lt;/p&gt;</description>
        </item>
        <item>
        <title>wrk vs. JMeter deep benchmarking</title>
        <link>https://ttf248.life/en/p/wrk-vs-jmeter-deep-benchmarking/</link>
        <pubDate>Fri, 19 Dec 2025 01:14:49 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/wrk-vs-jmeter-deep-benchmarking/</guid>
        <description>&lt;p&gt;In internet system stress testing, we frequently encounter two tools with vastly different styles: one is extremely lightweight, pursuing extreme throughput—wrk; the other is feature-rich and simulates real business flows—JMeter.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Prompt: Outline the core ideas and write a科普 article (explanatory article): HTTP stress testing tools, wrk vs JMeter – what are the differences? What I know, wrk tends to use one thread with multiple connections for testing, while JMeter primarily employs a short connection mode, which can be adjusted via configuration to enable long polling.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;core-architecture-multi-threaded-vs-event-driven&#34;&gt;Core Architecture: Multi-Threaded vs. Event-Driven
&lt;/h2&gt;&lt;p&gt;This is the fundamental reason for the performance gap between them.&lt;/p&gt;
&lt;h3 id=&#34;1-jmeter-the-one-person-one-job-thread-per-request-model&#34;&gt;1. JMeter: The “One Person, One Job” (Thread-per-Request) Model
&lt;/h3&gt;&lt;p&gt;JMeter is developed in Java and utilizes the classic &lt;strong&gt;multi-threaded model&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Logic:&lt;/strong&gt; Each concurrent user (Virtual User) corresponds to a physical thread within the JVM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost:&lt;/strong&gt; Threads are an expensive resource. As concurrency increases to several thousand, context switching and memory consumption will significantly slow down the test machine itself, leading to the phenomenon of “the load generator collapsing first before it can actually crush the server.”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-wrk-the-modern-multi-hand-system-event-driven&#34;&gt;2. wrk: The Modern “Multi-Hand” System (Event-driven)
&lt;/h3&gt;&lt;p&gt;wrk is written in C and its core logic relies on Redis’s &lt;code&gt;ae&lt;/code&gt; event loop framework (utilizing &lt;code&gt;epoll/kqueue&lt;/code&gt;).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Logic:&lt;/strong&gt; wrk doesn&amp;rsquo;t create a thread for each connection. It only starts a small number of threads (typically equal to the number of your CPU cores), and each thread manages thousands upon thousands of connections simultaneously through &lt;strong&gt;non-blocking I/O&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; This is what you referred to as “one thread, multiple connections.” It drastically reduces thread switching overhead, allowing single machines to achieve millions of RPS (Requests Per Second).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;connection-models-skip-connections-vs-dense-connections&#34;&gt;Connection Models: Skip Connections vs. Dense Connections
&lt;/h2&gt;&lt;p&gt;Regarding the connection patterns you mentioned, here’s a deeper level of detail:&lt;/p&gt;
&lt;h3 id=&#34;1-jmeters-heavy-and-light&#34;&gt;1. JMeter’s “Heavy” and “Light”
&lt;/h3&gt;&lt;p&gt;JMeter defaults to simulating real user behavior.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Short Connection Bias:&lt;/strong&gt; In default configurations, some older versions or specific configurations of JMeter may not actively reuse connections, leading to numerous TCP handshakes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tunability:&lt;/strong&gt; You can enable long connection by checking the &lt;strong&gt;“KeepAlive”&lt;/strong&gt; option in the &lt;code&gt;HTTP Request&lt;/code&gt; or adjusting connection pool parameters in the &lt;code&gt;user.properties&lt;/code&gt; file. However, even with long connections, limited by its thread model, it struggles to maintain tens of thousands of concurrent long connections.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-wrks-speed-and-power&#34;&gt;2. wrk’s “Speed” and “Power”
&lt;/h3&gt;&lt;p&gt;wrk was designed with the intention of testing the performance of &lt;strong&gt;HTTP Keep-Alive&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Long Connection Strategy:&lt;/strong&gt; wrk establishes a specified number of connections at the start of the test (&lt;code&gt;-c&lt;/code&gt; parameter) and attempts to reuse these connections throughout the entire test.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application Scenarios:&lt;/strong&gt; It’s particularly well-suited for testing the throughput limits of Nginx, gateways (Gateways), or high-concurrency APIs under extreme long connection pressure.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;deep-comparison-table&#34;&gt;Deep Comparison Table
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Feature&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;Development Language&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;C/Lua (Scripting)&lt;/td&gt;
					&lt;td&gt;Java (GUI)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;deep-comparison-table-1&#34;&gt;Deep Comparison Table
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Feature&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;Concurrency Model&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Event Driven (epoll/kqueue)&lt;/td&gt;
					&lt;td&gt;Multi-threaded (Thread-per-User)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;deep-comparison-table-2&#34;&gt;Deep Comparison Table
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Feature&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;Resource Consumption&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Extremely Low, Huge Throughput on a Single Machine&lt;/td&gt;
					&lt;td&gt;Higher, Requires Distributed Cluster for High Concurrency&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;deep-comparison-table-3&#34;&gt;Deep Comparison Table
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Feature&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;Business Complexity&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Low, primarily for single URLs&lt;/td&gt;
					&lt;td&gt;High, supports multi-step scripts, assertions, and extractors&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;deep-comparison-table-4&#34;&gt;Deep Comparison Table
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Feature&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;Test Scenarios&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Static API Load Testing, Capacity Assessment&lt;/td&gt;
					&lt;td&gt;Complex Business Link Simulations, Functional Regression Testing&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;deep-comparison-table-5&#34;&gt;Deep Comparison Table
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Feature&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;Reporting Capabilities&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Only text summaries&lt;/td&gt;
					&lt;td&gt;Extremely rich, supports various charts and HTML reports&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;summary-which-one-should-i-choose&#34;&gt;Summary: Which one should I choose?
&lt;/h2&gt;&lt;p&gt;These two tools are complementary rather than substitutional relationships:&lt;/p&gt;
&lt;h3 id=&#34;select-work&#34;&gt;Select Work
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Want to test the &lt;strong&gt;maximum throughput&lt;/strong&gt; (RPS) of the server.&lt;/li&gt;
&lt;li&gt;The testing object is a single API or static resource.&lt;/li&gt;
&lt;li&gt;Aim to push the largest traffic using the fewest test servers.&lt;/li&gt;
&lt;li&gt;Familiar with Lua scripting to customize requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;choose-jmeter&#34;&gt;Choose JMeter
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Requires simulating &lt;strong&gt;complex business processes&lt;/strong&gt; (such as: Login -&amp;gt; Search Products -&amp;gt; Place Order -&amp;gt; Payment).&lt;/li&gt;
&lt;li&gt;Needs a visual interface to observe response time distributions, error rates, and other detailed metrics.&lt;/li&gt;
&lt;li&gt;Testing requires handling dynamic parameters (such as extracting a Token from the previous interface and passing it to the next interface).&lt;/li&gt;
&lt;li&gt;The team is more accustomed to using graphical tools rather than command-line interfaces.&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>Detailed Explanation of How Passkeys Work and Their Future</title>
        <link>https://ttf248.life/en/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/en/p/detailed-explanation-of-how-passkeys-work-and-their-future/</guid>
        <description>&lt;p&gt;Background introduction: Tencent’s CNB platform only supports WeChat login, with no conventional email account method, leading to several guys in the group complaining about it daily – it&amp;rsquo;s getting annoying. The Tencent product manager came up with a compromise solution: support Passkey login.&lt;/p&gt;
&lt;p&gt;Every day, we are repeating a dangerous action: entering passwords. Despite our complex rules (uppercase, special symbols, numbers), data breaches, phishing attacks, and the frustration of “forgotten password” issues continue to trouble everyone.&lt;/p&gt;
&lt;p&gt;Tech giants (Apple, Google, Microsoft) along with the FIDO Alliance have provided the ultimate solution: &lt;strong&gt;Passkey (Passcode Key)&lt;/strong&gt;. It’s not just a “replacement” for passwords; it completely “eliminates” them.&lt;/p&gt;
&lt;p&gt;The login process has shifted from verifying passwords to verifying the &lt;strong&gt;current device&lt;/strong&gt;&amp;rsquo;s trustworthiness.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Explain how Passkey works, how it can be used for login management, collect relevant content, and output an article for internet publication&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;what-is-a-passkey&#34;&gt;What is a Passkey?
&lt;/h2&gt;&lt;p&gt;Simply put, &lt;strong&gt;a Passkey is a digital credential stored on your device.&lt;/strong&gt;
It replaces the traditional “username + password.” When you log into websites that support Passkeys (such as Google, GitHub, Adobe), you don’t need to enter any characters – you just verify using facial recognition (Face ID), fingerprint (Touch ID), or device PIN, and you’re instantly logged in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key Difference:&lt;/strong&gt; A password is a string of characters &lt;strong&gt;you remember&lt;/strong&gt; (easily stolen, guessed, or forgotten); a Passkey is an &lt;strong&gt;asset you own&lt;/strong&gt; – an encrypted key stored within the device hardware.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;how-passkeys-work-asymmetric-encryption&#34;&gt;How Passkeys Work: Asymmetric Encryption
&lt;/h2&gt;&lt;p&gt;Passkey is built on the &lt;strong&gt;WebAuthn&lt;/strong&gt; standard and the &lt;strong&gt;FIDO2&lt;/strong&gt; protocol. To understand it, we need to understand &lt;strong&gt;Public Key Cryptography&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Imagine a passkey as a pair of “keys” and “locks”:&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;Where is it stored?&lt;/strong&gt; Securely stored on your device (such as iPhone’s secure enclave, computer’s TPM module, or password manager).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Characteristics:&lt;/strong&gt; Extremely sensitive, &lt;strong&gt;never sent to the server&lt;/strong&gt;, and doesn&amp;rsquo;t leave your device.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Function:&lt;/strong&gt; It’s your “digital signature pen.”&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;Where is it stored?&lt;/strong&gt; Uploaded and stored on the website/app’s server.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Characteristics:&lt;/strong&gt; Publicly visible, not sensitive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Function:&lt;/strong&gt; It&amp;rsquo;s used to verify your signature – like a “money checker.”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;handshake-details-the-process&#34;&gt;Handshake Details (The Process)
&lt;/h3&gt;&lt;p&gt;When you log in using a Passkey, a sophisticated “challenge-response” mechanism takes place:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initiate Login:&lt;/strong&gt; You click &amp;ldquo;Login,&amp;rdquo; and the website server sends a &lt;strong&gt;random mathematical problem (Challenge)&lt;/strong&gt; to your device.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Local Verification:&lt;/strong&gt; Your phone/computer displays a prompt requesting you unlock it using biometrics (face recognition/fingerprint).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Note: This step is solely for authorizing the device to use its private key; biometric data itself is not uploaded.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Digital Signature:&lt;/strong&gt; Upon successful unlocking, the device uses its &lt;strong&gt;private key&lt;/strong&gt; to “sign” the mathematical problem and sends the signature result back to the server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Server Verification:&lt;/strong&gt; The server uses the &lt;strong&gt;public key&lt;/strong&gt; you previously provided to verify this signature. If it’s valid, the server confirms that &amp;ldquo;the person who has validated the key indeed holds the private key,&amp;rdquo; thereby allowing login.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;why-passkeys-are-significantly-more-secure-than-passwords&#34;&gt;Why Passkeys Are Significantly More Secure Than Passwords?
&lt;/h2&gt;&lt;p&gt;Passkeys address the three major pain points of traditional passwords:&lt;/p&gt;
&lt;h3 id=&#34;completely-immune-to-phishing-attacks-anti-phishing&#34;&gt;Completely Immune to “Phishing Attacks” (Anti-Phishing)
&lt;/h3&gt;&lt;p&gt;This is the most robust feature of Passkey. The Passkey protocol enforces the inclusion of &lt;strong&gt;Origin Binding&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; A hacker creates a fake &lt;code&gt;g00gle.com&lt;/code&gt; to trick you into logging in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Result:&lt;/strong&gt; Your browser and system will detect that the current domain does not match the domain registered for your Passkey, &lt;code&gt;google.com&lt;/code&gt;, and &lt;strong&gt;refuse to initiate authentication&lt;/strong&gt;. You don’t even have a chance to “fall for it” and enter your password.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;server-leak-was-useless&#34;&gt;Server Leak Was Useless
&lt;/h3&gt;&lt;p&gt;Even if hackers breached Google’s servers and stole all the databases, they would only have obtained &lt;strong&gt;the public key&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The public key cannot be used to derive the private key.  Holding the public key is like holding a lock; but without the key (the private key which resides on your phone), the hacker couldn&amp;rsquo;t access your account.&lt;/p&gt;
&lt;h3 id=&#34;no-weak-passwords&#34;&gt;No “Weak Passwords”
&lt;/h3&gt;&lt;p&gt;Users no longer need to set weak passwords like “123456,” as the keys are strong, encrypted data generated by algorithms.&lt;/p&gt;
&lt;h2 id=&#34;how-passkeys-are-changing-login-management&#34;&gt;How Passkeys Are Changing “Login Management”?
&lt;/h2&gt;&lt;p&gt;Previously, we relied on tools like 1Password, LastPass, or Chrome browser to store lengthy strings of characters. Now, login management is undergoing a fundamental shift.&lt;/p&gt;
&lt;h3 id=&#34;cross-device-sync-passkey-sync&#34;&gt;Cross-Device Sync (Passkey Sync)
&lt;/h3&gt;&lt;p&gt;Early hardware keys (such as YubiKeys) are easily lost. Current Passkeys support cloud synchronization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Apple Ecosystem:&lt;/strong&gt; Through iCloud Keychain sync. Passkeys you create on your iPhone are automatically available on your Mac.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Google Ecosystem:&lt;/strong&gt; Through Google Password Manager sync for Android and Chrome.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Third-Party Management:&lt;/strong&gt; Tools like 1Password, Dashlane, etc., fully support Passkey. This means you can seamlessly sign in across ecosystems (using a Passkey stored in your iPhone on a Windows computer).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cross-device-login-via-qr-code&#34;&gt;Cross-Device Login (via QR Code)
&lt;/h3&gt;&lt;p&gt;What do you do if you want to log in to a PC at an internet cafe (Windows) but your Passkey is on your iPhone?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select “Login via Another Device” on the web page.&lt;/li&gt;
&lt;li&gt;A QR code (FIDO Cross-Device Flow) appears on the screen.&lt;/li&gt;
&lt;li&gt;Use your iPhone’s camera to scan the QR code.&lt;/li&gt;
&lt;li&gt;Your phone establishes a Near Field Connection with the computer via Bluetooth (to verify you are present) and performs biometric authentication.&lt;/li&gt;
&lt;li&gt;Login is successful on the PC.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;from-managing-secrets-to-managing-trust&#34;&gt;From “Managing Secrets” to “Managing Trust”
&lt;/h3&gt;&lt;p&gt;Future login management will no longer be about checking individual plaintext passwords, but rather &lt;strong&gt;managing trusted devices&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You can see: “My GitHub account is bound to my iPhone and MacBook.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If your phone is lost or stolen, you simply revoke the device’s public key access rights from the server (or cloud account).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;table-comparison-password-vs-passkey&#34;&gt;Table Comparison: Password vs. Passkey
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Dimension&lt;/th&gt;
					&lt;th&gt;Traditional Password&lt;/th&gt;
					&lt;th&gt;Passkey&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Cognitive Burden&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;High (Requires memorizing complex characters)&lt;/td&gt;
					&lt;td&gt;None (No need to memorize)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;table-comparison-passwords-vs-passkeys&#34;&gt;Table Comparison: Passwords vs. Passkeys
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Dimension&lt;/th&gt;
					&lt;th&gt;Traditional Passwords&lt;/th&gt;
					&lt;th&gt;Passkeys&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Phishing Risk&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;High (Susceptible to being tricked into entering)&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Zero&lt;/strong&gt; (Domain Binding)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;table-comparison-passwords-vs-passkeys-1&#34;&gt;Table Comparison: Passwords vs. Passkeys
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Dimension&lt;/th&gt;
					&lt;th&gt;Traditional Passwords&lt;/th&gt;
					&lt;th&gt;Passkeys&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Server Leakage&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Dangerous (Requires Brute-Force / Password Reset)&lt;/td&gt;
					&lt;td&gt;Secure (Key Leakage Has No Impact)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;table-comparison-password-vs-passkey-1&#34;&gt;Table Comparison: Password vs. Passkey
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Dimension&lt;/th&gt;
					&lt;th&gt;Traditional Password&lt;/th&gt;
					&lt;th&gt;Passkey&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Login Experience&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Slow (typing or copy-pasting)&lt;/td&gt;
					&lt;td&gt;Fast (one-tap biometric verification)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;table-comparison-password-vs-passkey-2&#34;&gt;Table Comparison: Password vs. Passkey
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Dimension&lt;/th&gt;
					&lt;th&gt;Traditional Password&lt;/th&gt;
					&lt;th&gt;Passkey&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;Reliance&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Relies on memory or a password manager&lt;/td&gt;
					&lt;td&gt;Relies on device (phone/computer)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;current-challenges-and-future&#34;&gt;Current Challenges and Future
&lt;/h2&gt;&lt;p&gt;Despite the great potential of Passkeys, widespread adoption still requires time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Platform Barriers:&lt;/strong&gt; While standards are unified, Apple, Google, and Microsoft offer the smoothest experiences within their respective ecosystems. Cross-ecosystem transitions (such as an Android phone paired with an iPad) are feasible but still involve some friction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Device Dependency:&lt;/strong&gt; Losing all trusted devices without a cloud backup can make account recovery difficult (typically requiring a backup recovery code).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Legacy System Compatibility:&lt;/strong&gt; Many older websites and internal corporate networks do not yet support the WebAuthn standard.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary
&lt;/h2&gt;&lt;p&gt;Passkey isn’t an upgrade to passwords; it&amp;rsquo;s a &lt;strong&gt;fundamental reconstruction&lt;/strong&gt; of internet identity verification. It leverages modern device biometric capabilities and public-key cryptography, elevating security to a financial level while simplifying the user experience to the extreme.&lt;/p&gt;
&lt;p&gt;For average users, enabling Passkeys on supported platforms (Google, Apple, Microsoft, Amazon, etc.) is the highest return on investment for improving personal digital security.&lt;/p&gt;</description>
        </item>
        <item>
        <title>Is AI Getting “Dumber”? Uncovering the Game Between Parameter Accuracy and Inference Costs</title>
        <link>https://ttf248.life/en/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/en/p/is-ai-getting-dumber-uncovering-the-game-between-parameter-precision-and-inference-costs/</guid>
        <description>&lt;p&gt;Recently mingling in various programming large model communication circles, the most complained about thing is model degradation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Models deployed on local desktop computers are quantized models, essentially downgraded versions.&lt;/li&gt;
&lt;li&gt;With “vibe coding” so popular, could it be that the content output by current large models is the most valuable product – code?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This round of prompts received one optimization, which coincided with model degradation, and the large model’s solution was to optimize the prompt, with more detailed task planning and clearer output requirements.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Addressing this issue, provide suitable prompts: Many large factories have now provided large model services, and users sometimes feedback that models are degrading; from a professional perspective, considering parameter precision and inference cost, write an article;科普文 (科普 article), don’t be too long.
Optimized version:&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;You are a senior AI industry technical column writer. Your goal is to write a professional but accessible Chinese科普 article &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; general readers.
&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;Please step by step think and write:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1. Outline drafting: First, please draft a clear three-paragraph article structure outline &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;e.g., Introduction, Precision Analysis, Cost &lt;span class=&#34;p&#34;&gt;&amp;amp;&lt;/span&gt; Architecture Analysis, Conclusion&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;2. Content focus: The article must deeply and clearly explain the phenomenon of “model degradation” reported by users, and the core analysis points must revolve around **parameter precision &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;quantization&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;** and **inference operational cost**.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3. Word count and format: The final article length must be controlled at &lt;span class=&#34;m&#34;&gt;600&lt;/span&gt; words or less. Please use clear &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;##` headings to divide paragraphs, and use **bold** to emphasize key professional terms (such as: quantization, MoE, FP16).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;introduction&#34;&gt;Introduction
&lt;/h2&gt;&lt;p&gt;To ensure clarity and align with a科普 (explanatory) approach, I’ve established the following structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Introduction:&lt;/strong&gt; Confirming user perception of reality and introducing the core conflict – in the context of rapidly growing user scales, service providers must balance “intellectual peak” with “service availability.”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Precision Analysis (Core Point 1):&lt;/strong&gt; Explaining the relationship between &lt;strong&gt;parameters&lt;/strong&gt; and &lt;strong&gt;precision&lt;/strong&gt;. By comparing &lt;strong&gt;FP16&lt;/strong&gt; with &lt;strong&gt;quantization&lt;/strong&gt; (INT8/INT4), we use the analogy of &amp;ldquo;image compression&amp;rdquo; to illustrate why a drop in precision leads to degraded logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost &amp;amp; Architecture (Core Point 2):&lt;/strong&gt; Analyzing &lt;strong&gt;inference cost&lt;/strong&gt;. We introduce the &lt;strong&gt;MoE&lt;/strong&gt; architecture and &lt;strong&gt;sparse activation&lt;/strong&gt; mechanisms, explaining how models “slack off” to save money and accelerate performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; Summarizing that this &amp;ldquo;degradation of intelligence&amp;rdquo; is an inevitable path for commercialization and widespread adoption (the impossibility triangle).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;article-body&#34;&gt;Article Body
&lt;/h2&gt;&lt;p&gt;Recently, many users have reported that large models seem less “intelligent” than before: logic has become simpler, instruction following has deteriorated, and even empty talk (“car轱辘话”) has emerged. This isn’t entirely an illusion. From a technical perspective, this “decline in intelligence” phenomenon is often a technological compromise made by major companies when facing massive user bases, balancing &lt;strong&gt;inference costs&lt;/strong&gt; with service speed.&lt;/p&gt;
&lt;h2 id=&#34;precision-shrinking-the-cost-of-4k-to-720p&#34;&gt;Precision “Shrinking”: The Cost of 4K to 720P
&lt;/h2&gt;&lt;p&gt;The “intelligence” of large models is primarily stored in trillions of &lt;strong&gt;parameters&lt;/strong&gt;. Ideally, these parameters run in high-precision &lt;strong&gt;FP16&lt;/strong&gt; (16-bit floating-point) format, capturing extremely subtle semantic differences. However, this high precision means significant &lt;strong&gt;memory&lt;/strong&gt; usage and slow computation speeds.&lt;/p&gt;
&lt;p&gt;To allow hundreds of millions of users to use the service smoothly, service providers generally adopt &lt;strong&gt;quantization&lt;/strong&gt; technology. This is a technique that compresses parameter precision from &lt;strong&gt;FP16&lt;/strong&gt; to &lt;strong&gt;INT8&lt;/strong&gt; or even &lt;strong&gt;INT4&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is like compressing a 4K high-definition movie into a 720P stream: although the plot (overall logic) hasn&amp;rsquo;t changed, the visual details (small logical associations, complex instruction execution details) are lost. This “lossy compression” directly leads to a decline in model performance when handling complex tasks, giving users the sensation of “getting dumber.”&lt;/p&gt;
&lt;h2 id=&#34;cost-pressure-letting-the-brain-partially-rest&#34;&gt;Cost Pressure: Letting the Brain “Partially Rest”
&lt;/h2&gt;&lt;p&gt;Besides accuracy, &lt;strong&gt;the operational cost of inference&lt;/strong&gt; is another key factor. Every time you ask an AI a question, servers perform massive matrix calculations, leading to exorbitant electricity bills and hardware wear and tear.&lt;/p&gt;
&lt;p&gt;To reduce costs, modern large models often adopt &lt;strong&gt;MoE&lt;/strong&gt; (Mixture of Experts) architecture. Unlike traditional models that activate all neurons every time, &lt;strong&gt;MoE&lt;/strong&gt; employs a &lt;strong&gt;sparse activation&lt;/strong&gt; strategy: for your question, the system only activates a small subset of relevant “expert” networks, while the rest remain dormant.&lt;/p&gt;
&lt;p&gt;Although this significantly reduces computational load, if the &lt;strong&gt;routing algorithm&lt;/strong&gt; (Router) distributes your complex mathematical problems to “literary experts” for load balancing or power saving, or sacrifices the number of expert calls in favor of speed, the output quality will inevitably fluctuate and decline.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Therefore, what is being referred to as “diminishment” – in essence, is the inevitable pain associated with AI’s transition from the laboratory to large-scale commercialization. The restraint on &lt;strong&gt;parameter precision&lt;/strong&gt; and the efficient design of &lt;strong&gt;model architecture&lt;/strong&gt; are what manufacturers seek to balance between “absolute intelligence” and “cost efficiency,” allowing everyone to affordably use AI.&lt;/p&gt;</description>
        </item>
        <item>
        <title>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.</title>
        <link>https://ttf248.life/en/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/en/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;This site’s main domain is hosted on GitHub Pages, and the blog’s backup subdomain is on Vercel with acceleration. It has been deployed through Cloudflare. The backend management page looks too outdated and unappealing, which is a major deterrent. Initially, it was a quiet, unassuming piece of content; however, constant messages in the blogging circles disrupted this tranquility – opening it up, CF went down, and many friends’ websites couldn&amp;rsquo;t be accessed.&lt;/p&gt;
&lt;p&gt;As an internet infrastructure component, such prolonged downtime is unacceptable. The stock price didn’t plummet dramatically, which I hadn’t anticipated.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cloudflare outage; many well-known websites are inaccessible. Pre-event stock drop, compiling relevant content, outputting articles&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;November 18, 2025 (Tuesday) –&lt;/strong&gt; This morning (November 18th), Cloudflare, a major global internet infrastructure services provider, experienced a widespread network outage before the opening of U.S. markets, causing thousands of well-known websites and applications that rely on its services to become paralyzed or difficult to access worldwide.&lt;/p&gt;
&lt;p&gt;As a result of this incident, Cloudflare’s (NYSE: NET) stock price fell sharply during pre-market trading.&lt;/p&gt;
&lt;h2 id=&#34;-outage-affects-global-services-as-popular-platforms-go-down&#34;&gt;🌍 Outage Affects Global Services as Popular Platforms “Go Down”
&lt;/h2&gt;&lt;p&gt;The outage began on Tuesday morning (approximately 7:00 AM Eastern Time, 11:48 GMT) and was reported globally. Users across the world were unable to access a number of popular services including social media platform &lt;strong&gt;X (formerly Twitter)&lt;/strong&gt;, artificial intelligence service &lt;strong&gt;OpenAI (ChatGPT)&lt;/strong&gt;, music streaming service &lt;strong&gt;Spotify&lt;/strong&gt;, game platforms &lt;strong&gt;League of Legends&lt;/strong&gt; and &lt;strong&gt;Valorant&lt;/strong&gt;, as well as numerous cryptocurrency exchanges (such as BitMEX, DefiLlama), among others.
Users attempting to access these websites generally encountered “500 Internal Server Error” messages. Given Cloudflare’s role as an “intermediary” in the internet, providing content delivery (CDN) and security protection (DDoS) for millions of websites worldwide, any “jitter” within its network immediately triggered a global chain reaction.&lt;/p&gt;
&lt;h2 id=&#34;-market-reaction-was-rapid-net-pre-market-stock-drop&#34;&gt;📉 Market Reaction Was Rapid, (NET) Pre-Market Stock Drop
&lt;/h2&gt;&lt;p&gt;As a key infrastructure component for the internet, Cloudflare’s service stability is of significant interest to investors. Following the release of the outage news, the capital markets reacted quickly. Cloudflare&amp;rsquo;s (NYSE: NET) stock experienced heavy selling in pre-market trading on Tuesday. According to financial data, its price fell approximately &lt;strong&gt;3.5% to 4%&lt;/strong&gt;, dropping from a previous day’s close of around $202.25 on November 17th to levels as low as $195.&lt;/p&gt;
&lt;p&gt;This reflects investors&amp;rsquo; concerns regarding the severity of the service interruption and its potential impact on the company’s reputation and short-term revenue.&lt;/p&gt;
&lt;h2 id=&#34;official-response-issue-identified-and-under-repair&#34;&gt;Official Response: Issue Identified and Under Repair
&lt;/h2&gt;&lt;p&gt;Cloudflare’s official team quickly confirmed this incident on its System Status page. The company updated the status at 13:09 UTC (November 18th) this afternoon (Beijing time) stating:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The issue has been identified and remediation is being implemented.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;-official-response-issue-identified-and-under-repair&#34;&gt;🛠️ Official Response: Issue Identified and Under Repair
&lt;/h2&gt;&lt;p&gt;According to Cloudflare’s report, this incident has been classified as a “global network experiencing issues” (global network encountering problems) and accompanied by “widespread 500 errors.”&lt;/p&gt;
&lt;p&gt;Cloudflare stated that they are working to mitigate the issue and have observed service beginning to recover, but cautioned that customers may continue to observe “above normal error rates” during ongoing repair work.&lt;/p&gt;
&lt;p&gt;As of this writing, access to some websites has begun to gradually restore, but global network latency and error rates remain high. We will continue to monitor the situation’s further development.&lt;/p&gt;</description>
        </item>
        <item>
        <title>Understanding MySQL Gaps Locks: From Principles to Enterprise-Grade Selection</title>
        <link>https://ttf248.life/en/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/en/p/understanding-mysql-gaps-locks-from-principles-to-enterprise-grade-selection/</guid>
        <description>&lt;p&gt;System data synchronization between systems, recently when changes occurred, the data volume of a certain interface increased, frequent deadlocks happened. After colleagues investigated, they found that it was caused by gap locks. MySQL is not used much, so I’ll record it down.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;mysql gap locks, background, principle, how to disable gap locks, what problems will occur if disabled; you are a senior database DBA, provide an extended analysis and supplement for my problem. What is phantom read? Which RC or RR mode do mainstream large factories choose? I am in the financial industry, securities system, which scheme do you recommend me to use? Compile the above questions and output documentation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In MySQL, especially in high-concurrency scenarios, “locks” are a topic that cannot be avoided. Gap Locks are a core feature of the InnoDB engine, but they are also often the root cause of performance bottlenecks and deadlocks.&lt;/p&gt;
&lt;p&gt;This article will help you quickly understand the core, pros and cons of gap locks, and provide you (especially those in the financial industry) with clear architectural selection recommendations.&lt;/p&gt;
&lt;h2 id=&#34;what-are-phantom-reads-and-gap-locks&#34;&gt;What are Phantom Reads and Gap Locks?
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Phantom Read:&lt;/strong&gt;
In a transaction, you initially read a range of data (e.g., &lt;code&gt;ID &amp;gt; 100&lt;/code&gt;) returning 10 records. After another transaction inserts a record with &lt;code&gt;ID=101&lt;/code&gt; and commits, when you re-read the same range, it now returns 11 records. This &amp;ldquo;extra&amp;rdquo; new record is like an illusion, hence the name &amp;ldquo;Phantom Read.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gap Lock:&lt;/strong&gt;
MySQL introduces the Gap Lock in the &lt;strong&gt;RR (Repeatable Read)&lt;/strong&gt; isolation level to address the issue of &amp;ldquo;Phantom Reads.&amp;rdquo; It doesn&amp;rsquo;t lock individual data rows; instead, it locks the &amp;ldquo;gaps&amp;rdquo; between data.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; To prevent other transactions from inserting new data into these &amp;ldquo;gaps.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost:&lt;/strong&gt; The scope of the lock expands, reducing concurrency and increasing the risk of deadlocks.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-to-close-gap-locks&#34;&gt;How to &amp;ldquo;Close&amp;rdquo; Gap Locks?
&lt;/h2&gt;&lt;p&gt;The most direct method is to &lt;strong&gt;lower the database isolation level from RR (Repeatable Read) to 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;-- Set the current session isolation level to 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;-- Permanent modification (requires modifying my.cnf and restarting)
&lt;/span&gt;&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;-consequences-of-closure-ie-rc-level-features&#34;&gt;⚠️ Consequences of Closure (i.e., RC-level Features)
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Improvement:&lt;/strong&gt; Without gap locks, the granularity of locks is smaller, resulting in a significant increase in concurrency throughput.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced Deadlocks:&lt;/strong&gt; Most deadlocks caused by gap locks will disappear.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Phantom Reads/Non-Repeatable Reads:&lt;/strong&gt; This is a “feature” of RC-level features – results of two queries within a transaction may be inconsistent.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;[Core Requirement] Must Be Used with ROW Format Binlog:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If using RC-level, &lt;strong&gt;it’s mandatory&lt;/strong&gt; to set &lt;code&gt;binlog_format&lt;/code&gt; to &lt;code&gt;ROW&lt;/code&gt;. Otherwise, due to the lack of gap lock protection during master-slave replication, data inconsistency will occur between the master and slave.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;mainstream-factory-choices-rc-or-rr&#34;&gt;Mainstream Factory Choices: RC or RR?
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; The vast majority of leading internet companies choose RC (Read Committed) + ROW Binlog mode.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reason:&lt;/strong&gt; Internet businesses (such as e-commerce and social media) pursue extreme high concurrency. They cannot tolerate the frequent lock waiting and deadlocks caused by RR gap locks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trade-off:&lt;/strong&gt; They choose to abandon database-level “repeatable read,” instead relying on optimistic locking (such as version numbers, CAS) at the application layer to ensure the logical consistency of key business areas (such as inventory and balance).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-should-the-financial-industry-brokerage-firms-choose&#34;&gt;How Should the Financial Industry (Brokerage Firms) Choose?
&lt;/h2&gt;&lt;p&gt;For brokerage systems with extremely high data consistency requirements, it is recommended to &lt;strong&gt;implement scenario-based governance&lt;/strong&gt;:&lt;/p&gt;
&lt;h3 id=&#34;option-1-core-trading-system-high-concurrency-low-latency&#34;&gt;Option 1: Core Trading System (High Concurrency, Low Latency)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Recommendation:&lt;/strong&gt; RC (Read Committed) + ROW Binlog + Application-Level Optimistic Locking&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Order Matching, Placing Orders, Funds Deduction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reasoning:&lt;/strong&gt; The concurrency pressure of trading systems is comparable to that of flash sales on the internet. RR’s gap locking will become a performance bottleneck, leading to severe lock competition and even deadlocks – something a trading system cannot tolerate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Countermeasures:&lt;/strong&gt; Use RC to guarantee high performance while ensuring financial security within application code (e.g., in Java) through &lt;code&gt;UPDATE ... WHERE balance &amp;gt; ?&lt;/code&gt; or using CAS version number mechanisms to prevent overselling.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;option-two-clearing-and-reconciliation-system-batch-processing-high-consistency&#34;&gt;Option Two: Clearing and Reconciliation System (Batch Processing, High Consistency)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Recommendation:&lt;/strong&gt; RR (Repeatable Read)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Post-settlement bulk reconciliation, report generation, daily final settlement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reasoning:&lt;/strong&gt; Batch processing tasks require a “consistent snapshot” to run on. It needs the capabilities provided by RR level, ensuring that data does not experience “phantom reads” throughout the entire statistical process, guaranteeing the general ledger is accurate.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Isolation Level&lt;/th&gt;
					&lt;th&gt;Advantages&lt;/th&gt;
					&lt;th&gt;Disadvantages&lt;/th&gt;
					&lt;th&gt;Suitable Scenarios&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;RR (Default)&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Solves phantom reads, high data consistency&lt;/td&gt;
					&lt;td&gt;Low concurrency, prone to deadlocks (gap locks)&lt;/td&gt;
					&lt;td&gt;Reporting, clearing, scenarios with extremely high data consistency requirements and low concurrency&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;summary-1&#34;&gt;Summary
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Isolation Level&lt;/th&gt;
					&lt;th&gt;Advantages&lt;/th&gt;
					&lt;th&gt;Disadvantages&lt;/th&gt;
					&lt;th&gt;Use Cases&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;RC&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;High Concurrency, Fewer Deadlocks&lt;/td&gt;
					&lt;td&gt;Phantom Reads, Non-Repeatable Reads&lt;/td&gt;
					&lt;td&gt;High Concurrency Core Business (e.g., E-commerce, Financial Transactions), Combined with ROW Binlog&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;summary-2&#34;&gt;Summary
&lt;/h2&gt;&lt;p&gt;For securities broker core systems, &lt;strong&gt;RC + ROW Binlog&lt;/strong&gt; is the mainstream architecture solution that balances performance and consistency; however, it requires the development team to take on more responsibility for ensuring data consistency at the application layer.&lt;/p&gt;</description>
        </item>
        <item>
        <title>Local Development Pain: Why Can&#39;t You Delete `nul` Files? A Solution to the “Composite” File System Problem</title>
        <link>https://ttf248.life/en/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/en/p/local-development-pain-why-cant-you-delete-nul-files-a-solution-to-the-composite-file-system-problem/</guid>
        <description>&lt;p&gt;In the daily work of software development, we often encounter tricky “minor issues” that seem simple but can consume us hours of valuable time. One such “disaster zone” is deleting specific files – particularly those generated unexpectedly by toolchains.&lt;/p&gt;
&lt;p&gt;I encountered a “level-boss” problem: during local development, a file named &lt;code&gt;nul&lt;/code&gt; inexplicably appeared in my project. I tried Windows Explorer, the CMD command line, but the system displayed &amp;ldquo;File not found&amp;rdquo; or &amp;ldquo;Cannot delete.&amp;rdquo; This file was like a ghost, stubbornly residing in my project directory.&lt;/p&gt;
&lt;h2 id=&#34;phase-one-routine-attempts-and-the-ineffective-solution-of-standard-approaches&#34;&gt;Phase One: Routine Attempts and the Ineffective Solution of “Standard” Approaches
&lt;/h2&gt;&lt;p&gt;When I encounter this problem, my first instinct is to create a &lt;code&gt;nul&lt;/code&gt; file.
&lt;strong&gt;Why is &lt;code&gt;nul&lt;/code&gt; special?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Developers familiar with Windows history may know that &lt;code&gt;nul&lt;/code&gt; is a &amp;ldquo;trap.&amp;rdquo; In Windows (and earlier DOS) systems, &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;, etc., are reserved device names. &lt;code&gt;NUL&lt;/code&gt; represents the “null device” (similar to Unix/Linux’s &lt;code&gt;/dev/null&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;When the Windows file system API sees you trying to operate a “file” named &lt;code&gt;nul&lt;/code&gt;, it treats it as an operation on this “null device,” rather than a filename with the same name. Therefore, all standard file operations (such as deleting or renaming) fail.
&lt;strong&gt;How are &lt;code&gt;nul&lt;/code&gt; files created?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is usually the fault of cross-platform development tools (like Git, Node.js scripts, Python scripts, etc.). These tools may be based on POSIX (Unix-like) standards, in which case &lt;code&gt;nul&lt;/code&gt; is simply a regular filename to them. When they run on Windows, they sometimes bypass standard APIs and create this “Windows-unfriendly” file.
&lt;strong&gt;“Standard” Solutions Recommended Online&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I quickly searched the web and found that I wasn’t the first person to encounter this problem. The community offered several “advanced” solutions that were widely accepted:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use the &lt;code&gt;\\.\&lt;/code&gt; syntax:&lt;/strong&gt; In CMD, use the special &amp;ldquo;long path&amp;rdquo; syntax to bypass Windows name checking.&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;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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Git Bash:&lt;/strong&gt; Git Bash provides a lightweight Unix environment, which doesn’t treat &lt;code&gt;nul&lt;/code&gt; as a special device.&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;rm nul
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use WSL (Windows Subsystem for Linux):&lt;/strong&gt; Enter WSL, mount the Windows disk, and use the Linux &lt;code&gt;rm&lt;/code&gt; command to delete.&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;rm /mnt/c/your/project/path/nul
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, &lt;strong&gt;none of these methods worked for me!&lt;/strong&gt; Whether it was WSL or Git Bash, when I tried &lt;code&gt;rm nul&lt;/code&gt;, the system reported a &amp;ldquo;No such file or directory&amp;rdquo; error. This led me to ponder the problem seemed more complex than I initially thought.&lt;/p&gt;
&lt;h2 id=&#34;stage-two-a-flash-of-insight--is-it-multiple-problem-overlapping&#34;&gt;Stage Two: A Flash of Insight – Is it “Multiple Problem Overlapping”?
&lt;/h2&gt;&lt;p&gt;If the &lt;code&gt;nul&lt;/code&gt; file actually existed, why were even Unix tools saying &amp;ldquo;cannot find&amp;rdquo; it?&lt;/p&gt;
&lt;p&gt;I began to suspect that &lt;strong&gt;the problem wasn&amp;rsquo;t just with the &lt;code&gt;nul&lt;/code&gt; file itself, but also with its “habitat” – the directory where it was located?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I immediately opened Git Bash (this was key, because Windows Explorer might not display anomalies), navigated to the directory containing the &lt;code&gt;nul&lt;/code&gt; file, and then executed &lt;code&gt;ls -la&lt;/code&gt; (list all files, including hidden ones, and show detailed information).&lt;/p&gt;
&lt;p&gt;That’s when I finally discovered the “blind spot”: &lt;strong&gt;the directory where the &lt;code&gt;nul&lt;/code&gt; file was stored actually contained an illegal character within its name!&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;stage-two-a-flash-of-insight--is-it-multiple-problem-overlap&#34;&gt;Stage Two: A Flash of Insight – Is it “Multiple Problem Overlap”?
&lt;/h2&gt;&lt;p&gt;In my case, this directory name might be one ending with a space or a period (&lt;code&gt;.&lt;/code&gt;) or containing special characters (like &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;:&lt;/code&gt;) that development tools “carry along” when syncing across platforms—these are all equally problematic.&lt;/p&gt;
&lt;p&gt;For example, a directory displayed in Git Bash as &lt;code&gt;&amp;quot;my-app &amp;quot;&lt;/code&gt; (note the trailing space) or &lt;code&gt;&amp;quot;my-app.&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That’s where the problem lies!&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problem A:&lt;/strong&gt; I have an “illegal” file named &lt;code&gt;nul&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Problem B:&lt;/strong&gt; I have an “illegal” directory named &lt;code&gt;&amp;quot;my-app &amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I try &lt;code&gt;rm /path/to/&amp;quot;my-app &amp;quot;/nul&lt;/code&gt;, both the Windows system and Unix tools are “confused.” The Windows API cannot correctly parse this path containing illegal characters; while Git Bash or WSL might “see” this illegal directory, it may fail to access its internal &lt;code&gt;nul&lt;/code&gt; file due to complex path parsing issues.&lt;/p&gt;
&lt;h2 id=&#34;phase-three-cutting-off-the-root--targeting-the-path-eliminating-everything-in-one-go&#34;&gt;Phase Three: Cutting Off the Root – Targeting the Path, Eliminating Everything in One Go
&lt;/h2&gt;&lt;p&gt;Now that we’ve confirmed it was a problem with both “file path” and “filename,” the solution became clear: &lt;strong&gt;don&amp;rsquo;t try to delete that &lt;code&gt;nul&lt;/code&gt; file; instead, directly delete that “illegitimate” parent directory!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My final resolution steps were as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Open Git Bash:&lt;/strong&gt; This is the only tool capable of correctly “seeing” and handling these illegal names.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Navigate to the parent directory of the “problem directory”:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify the true name of the “problem directory”:&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Execute the “Ultimate Delete”:&lt;/strong&gt; Using the &lt;code&gt;rm&lt;/code&gt; command with the &lt;code&gt;-r&lt;/code&gt; (recursive) and &lt;code&gt;-f&lt;/code&gt; (force) options, combined with &lt;strong&gt;quotes&lt;/strong&gt;, to delete the entire directory.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After executing the command, the troublesome directory containing the &lt;code&gt;nul&lt;/code&gt; file, which itself had an invalid name, was finally completely removed from my filesystem.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Keepalived &#43; HAProxy for High Availability Load Balancing</title>
        <link>https://ttf248.life/en/p/keepalived-haproxy-for-high-availability-load-balancing/</link>
        <pubDate>Fri, 19 Sep 2025 09:45:55 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/keepalived-haproxy-for-high-availability-load-balancing/</guid>
        <description>&lt;p&gt;In modern internet architectures, high availability is a crucial consideration in system design. This article will detail how to use Keepalived and HAProxy to build a highly available load balancing cluster, ensuring service continuity and reliability.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The practical configuration section was not validated, and the article planning relies on AI completion&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;Task Planning&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;125&#34;
		data-flex-basis=&#34;300px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;(Since I cannot see the image, I&amp;rsquo;m simply repeating the markdown as it was provided.)&lt;/p&gt;
&lt;h2 id=&#34;technical-overview&#34;&gt;Technical Overview
&lt;/h2&gt;&lt;h3 id=&#34;keepalived-introduction&#34;&gt;Keepalived Introduction
&lt;/h3&gt;&lt;p&gt;Keepalived is a high availability solution based on the VRRP (Virtual Router Redundancy Protocol) protocol, primarily used to implement server failover and load balancing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VRRP Protocol Support&lt;/strong&gt;: Enables virtual IP address master/slave switching&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Health Checks&lt;/strong&gt;: Monitors service status and automatically performs failover&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple Configuration&lt;/strong&gt;: Complex high availability architectures can be achieved through configuration files alone&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightweight&lt;/strong&gt;: Low resource consumption and excellent performance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Working Principle:&lt;/strong&gt;
Keepalived utilizes the VRRP protocol to share a virtual IP address across multiple servers. In normal operation, the master server holds the virtual IP and provides services; when the master server fails, the backup server automatically takes over the virtual IP, ensuring service continuity.&lt;/p&gt;
&lt;h3 id=&#34;haproxy-overview&#34;&gt;HAProxy Overview
&lt;/h3&gt;&lt;p&gt;HAProxy is a high-performance load balancer and reverse proxy server, widely used in high concurrency scenarios.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Features:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Load Balancing:&lt;/strong&gt; Supports various load balancing algorithms&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Health Checks:&lt;/strong&gt; Monitors backend server status in real-time&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSL Termination:&lt;/strong&gt; Supports HTTPS traffic handling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Statistical Monitoring:&lt;/strong&gt; Provides detailed running state statistics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Application Scenarios:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web service load balancing&lt;/li&gt;
&lt;li&gt;Database connection pooling&lt;/li&gt;
&lt;li&gt;Microservice gateways&lt;/li&gt;
&lt;li&gt;API interface proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;architecture-design&#34;&gt;Architecture Design
&lt;/h2&gt;&lt;h3 id=&#34;overall-architecture&#34;&gt;Overall Architecture
&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;component-description&#34;&gt;Component Description
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Virtual IP (VIP)&lt;/strong&gt;: A unified entry point for clients to access.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HAProxy Master-Backup Nodes&lt;/strong&gt;: Provides load balancing services and achieves high availability through Keepalived.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backend Servers&lt;/strong&gt;: The actual web servers providing the service.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;environment-setup&#34;&gt;Environment Setup
&lt;/h2&gt;&lt;h3 id=&#34;server-planning&#34;&gt;Server Planning
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Role&lt;/th&gt;
					&lt;th&gt;IP Address&lt;/th&gt;
					&lt;th&gt;Hostname&lt;/th&gt;
					&lt;th&gt;Service&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;HAProxy Master Node&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;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;server-planning-1&#34;&gt;Server Planning
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Role&lt;/th&gt;
					&lt;th&gt;IP Address&lt;/th&gt;
					&lt;th&gt;Hostname&lt;/th&gt;
					&lt;th&gt;Service&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;HAProxy Backup Node&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;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;server-planning-2&#34;&gt;Server Planning
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Role&lt;/th&gt;
					&lt;th&gt;IP Address&lt;/th&gt;
					&lt;th&gt;Hostname&lt;/th&gt;
					&lt;th&gt;Service&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Virtual 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;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;server-planning-3&#34;&gt;Server Planning
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Role&lt;/th&gt;
					&lt;th&gt;IP Address&lt;/th&gt;
					&lt;th&gt;Hostname&lt;/th&gt;
					&lt;th&gt;Service&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;server-planning-4&#34;&gt;Server Planning
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Role&lt;/th&gt;
					&lt;th&gt;IP Address&lt;/th&gt;
					&lt;th&gt;Hostname&lt;/th&gt;
					&lt;th&gt;Service&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;server-planning-5&#34;&gt;Server Planning
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Role&lt;/th&gt;
					&lt;th&gt;IP Address&lt;/th&gt;
					&lt;th&gt;Hostname&lt;/th&gt;
					&lt;th&gt;Service&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;software-installation&#34;&gt;Software Installation
&lt;/h3&gt;&lt;p&gt;Install the necessary software on the HAProxy master and backup nodes:&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;# Enable services to start automatically on boot&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-configuration&#34;&gt;Keepalived Configuration
&lt;/h2&gt;&lt;h3 id=&#34;master-node-configuration-lb-master&#34;&gt;Master Node Configuration (lb-master)
&lt;/h3&gt;&lt;p&gt;Create the configuration file &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;# Script to check HAProxy service status&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-configuration&#34;&gt;LB Backup Configuration
&lt;/h3&gt;&lt;p&gt;Create the configuration file &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;health-check-script&#34;&gt;Health Check Script
&lt;/h3&gt;&lt;p&gt;Create the HAProxy health check script &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;# Check if the haproxy process is running&lt;/span&gt;
&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;# Attempt to start 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;# Check again, if it&amp;#39;s still not running exit&lt;/span&gt;
&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;# Check if HAProxy port is listening&lt;/span&gt;
&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;state-notification-script&#34;&gt;State Notification Script
&lt;/h3&gt;&lt;p&gt;Create the state notification script &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;Set script execution permissions:&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-configuration&#34;&gt;HAProxy Configuration
&lt;/h2&gt;&lt;h3 id=&#34;main-configuration&#34;&gt;Main Configuration
&lt;/h3&gt;&lt;p&gt;Create the same HAProxy configuration file &lt;code&gt;/etc/haproxy/haproxy.cfg&lt;/code&gt; on the master node:&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;# Statistics page configuration&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;# Frontend configuration&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;# Backend server configuration&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;configuration-instructions&#34;&gt;Configuration Instructions
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Global Configuration:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;log&lt;/code&gt;: Log configuration&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chroot&lt;/code&gt;: Security sandbox&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stats socket&lt;/code&gt;: Management interface&lt;/li&gt;
&lt;li&gt;&lt;code&gt;daemon&lt;/code&gt;: Background execution&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Default Configuration:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mode http&lt;/code&gt;: HTTP mode&lt;/li&gt;
&lt;li&gt;&lt;code&gt;balance roundrobin&lt;/code&gt;: Round robin load balancing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;option httpchk&lt;/code&gt;: HTTP health check&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timeout&lt;/code&gt;: Various timeout settings&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Backend Servers:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;check&lt;/code&gt;: Enable health checks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;inter 2000&lt;/code&gt;: Check interval of 2 seconds&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rise 2&lt;/code&gt;: Mark as available after 2 consecutive successful checks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fall 3&lt;/code&gt;: Mark as unavailable after 3 consecutive failed checks&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;service-startup-and-testing&#34;&gt;Service Startup and Testing
&lt;/h2&gt;&lt;h3 id=&#34;start-service&#34;&gt;Start Service
&lt;/h3&gt;&lt;p&gt;Start the service on the master and backup nodes:&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;# Start 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;# Start 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;verify-vip-binding&#34;&gt;Verify VIP Binding
&lt;/h3&gt;&lt;p&gt;Check if the virtual IP is correctly bound:&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;# View IP address on the master node&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;# You should see output similar to:&lt;/span&gt;
&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;functional-testing&#34;&gt;Functional Testing
&lt;/h3&gt;&lt;h4 id=&#34;1-load-balancing-testing&#34;&gt;1. Load Balancing Testing
&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;# Repeatedly access the VIP and observe request distribution&lt;/span&gt;
&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-failover-testing&#34;&gt;2. Failover Testing
&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;# Stop the HAProxy service on the primary node&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;# Observe if the VIP switches to the backup node&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;# Test if the service is working normally&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-backend-server-failure-testing&#34;&gt;3. Backend Server Failure Testing
&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;# Stop one of the web servers&lt;/span&gt;
&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;# On the web1 server:&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;# Observe the HAProxy statistics page&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;monitoring-and-maintenance&#34;&gt;Monitoring and Maintenance
&lt;/h2&gt;&lt;h3 id=&#34;log-monitoring&#34;&gt;Log Monitoring
&lt;/h3&gt;&lt;h4 id=&#34;haproxy-logs&#34;&gt;HAProxy Logs
&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;# View HAProxy logs&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;# View access statistics&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-logs&#34;&gt;Keepalived Logs
&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;# View Keepalived logs&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;# View state change logs&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;performance-monitoring&#34;&gt;Performance Monitoring
&lt;/h3&gt;&lt;h4 id=&#34;statistical-page-monitoring&#34;&gt;Statistical Page Monitoring
&lt;/h4&gt;&lt;p&gt;Access the HAProxy statistics page: &lt;code&gt;http://192.168.1.100:8080/stats&lt;/code&gt;
Key Metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Session Rate&lt;/strong&gt;: Session rate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session Total&lt;/strong&gt;: Total number of sessions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bytes In/Out&lt;/strong&gt;: Traffic statistics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Response Time&lt;/strong&gt;: Response time&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Status&lt;/strong&gt;: Server status&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;command-line-monitoring&#34;&gt;Command Line Monitoring
&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;# Check HAProxy process status&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;# Check port listening status&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;# Check connection count&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;troubleshooting-faqs&#34;&gt;Troubleshooting FAQs
&lt;/h2&gt;&lt;h3 id=&#34;1-vip-cannot-be-switched&#34;&gt;1. VIP cannot be switched
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Problem Description:&lt;/strong&gt;
After the master node fails, the VIP does not switch to the backup node.
&lt;strong&gt;Troubleshooting Steps:&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;# Check Keepalived configuration&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;# View VRRP communication&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;# Check firewall settings&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;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensure that VRRP protocol communication is normal.&lt;/li&gt;
&lt;li&gt;Check network interface configuration.&lt;/li&gt;
&lt;li&gt;Verify authentication password consistency.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-health-check-failed&#34;&gt;2. Health Check Failed
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Problem Description:&lt;/strong&gt;
Backend server marked as unavailable
&lt;strong&gt;Troubleshooting Steps:&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;# Manually execute 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;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;# View HAProxy logs&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;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the health check URL is accessible&lt;/li&gt;
&lt;li&gt;Adjust the check interval and thresholds&lt;/li&gt;
&lt;li&gt;Check the backend server status&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-load-unbalance&#34;&gt;3. Load Unbalance
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Problem Description:&lt;/strong&gt;
Requests are not evenly distributed to the backend servers.
&lt;strong&gt;Troubleshooting Steps:&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;# View statistics page&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;# Analyze access logs&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;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check the load balancing algorithm configuration&lt;/li&gt;
&lt;li&gt;Verify server weights settings&lt;/li&gt;
&lt;li&gt;Consider session persistence requirements&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;optimization-suggestions&#34;&gt;Optimization Suggestions
&lt;/h2&gt;&lt;h3 id=&#34;1-performance-optimization&#34;&gt;1. Performance Optimization
&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;# Adjust system parameters&lt;/span&gt;
&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;# Optimize HAProxy configuration&lt;/span&gt;
&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;# Increase maxconn value&lt;/span&gt;
&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;# Adjust timeout parameters&lt;/span&gt;
&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;# Enable compression functionality&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-security-hardening&#34;&gt;2. Security Hardening
&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;# Restrict access to the statistics page&lt;/span&gt;
&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;# Add ACL rules in haproxy.cfg&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;# Enable 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-monitoring-and-alerts&#34;&gt;3. Monitoring and Alerts
&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;# Integrate with a monitoring system&lt;/span&gt;
&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;# Configure Prometheus for monitoring&lt;/span&gt;
&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;# Set up Grafana dashboards&lt;/span&gt;
&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;# Define alert rules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;summary&#34;&gt;Summary
&lt;/h2&gt;&lt;p&gt;By combining Keepalived and HAProxy, we successfully built a highly available load balancing cluster. This solution offers the following advantages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;High Availability:&lt;/strong&gt; Achieved through VRRP protocol for automatic failover.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Load Balancing:&lt;/strong&gt; Intelligently distributes requests to improve system performance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Health Checks:&lt;/strong&gt; Real-time monitoring of service status, automatically removing faulty nodes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ease of Maintenance:&lt;/strong&gt; Simple configuration and convenient management.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost Effectiveness:&lt;/strong&gt; Utilizing open-source software to reduce operational costs.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When deploying in a production environment, it’s also necessary to consider comprehensive aspects such as network security, monitoring and alerts, and backup/restore procedures to ensure stable and reliable operation of the system.&lt;/p&gt;</description>
        </item>
        
    </channel>
</rss>
