<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Jeanphilo Blog</title><link>https://jeanphilogong.github.io/jeanblog/zh/</link><description>Recent content on Jeanphilo Blog</description><generator>Hugo -- 0.168.0</generator><language>zh-cn</language><lastBuildDate>Sun, 25 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jeanphilogong.github.io/jeanblog/zh/index.xml" rel="self" type="application/rss+xml"/><item><title>分层之后，为什么每一层还在重复校验？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/validation-at-boundaries/</link><pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/validation-at-boundaries/</guid><description>用一个修改商品价格的例子，区分入口类型检查、领域业务约束与存储转换，减少分层系统中的重复校验和无目的抽象。</description></item><item><title>让 Agent 少写防御性代码：每条校验都要有理由</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/validation-needs-a-reason/</link><pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/validation-needs-a-reason/</guid><description>从策略工具的数组转换、反馈读取和查询筛选三个例子出发，判断哪些校验有实际用途，哪些只是重复工作，并给出约束 Agent 的编码规则。</description></item><item><title>Pydantic 的 field_validator：字段什么时候校验，返回值去了哪里？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pydantic-field-validator/</link><pubDate>Thu, 22 Oct 2026 17:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pydantic-field-validator/</guid><description>用考试成绩校验理解 Pydantic v2 的 field_validator：自动调用、返回值、ValueError 和校验顺序。</description></item><item><title>Skill、MCP 与 CLI：工具编排复杂了，就该上 MCP 吗？</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/skill-mcp-cli-boundaries/</link><pubDate>Thu, 22 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/skill-mcp-cli-boundaries/</guid><description>Skill 与 MCP 解决不同层次的复用问题，CLI 也没有因此过时。本文用代码审查场景解释三者的边界，以及何时应该抽取脚本、何时值得引入 MCP。</description></item><item><title>如何实现一次 Agent 工具调用</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/agent-tool-calling-with-responses-api/</link><pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/agent-tool-calling-with-responses-api/</guid><description>这篇文章只记录 Agent 工具调用的最小闭环：声明工具 schema、接收 function_call、执行本地函数，并通过 function_call_output 继续下一次模型决策。</description></item><item><title>LeetCode 1282：用户分组，从相同容量分桶到满组即输出</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1282-group-the-people-given-the-group-size-they-belong-to/</link><pubDate>Sat, 10 Oct 2026 10:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1282-group-the-people-given-the-group-size-they-belong-to/</guid><description>从用户编号与组大小的区别出发，先按容量收集再分块，逐步写出满组即输出的 Python 解法，解释列表引用陷阱、正确性与线性复杂度。</description></item><item><title>LeetCode 49：字母异位词分组，从逐组比较到构造分组键</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/49-group-anagrams/</link><pubDate>Sat, 10 Oct 2026 10:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/49-group-anagrams/</guid><description>用 Python 从两个字符串的异位词判断出发，先写出逐组扫描的正确解，再推导排序键与字符计数键，理解哈希表分组为什么成立。</description></item><item><title>LeetCode 609：在文件系统中查找重复文件，从解析记录到按内容分组</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/609-find-duplicate-file-in-system/</link><pubDate>Sat, 10 Oct 2026 10:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/609-find-duplicate-file-in-system/</guid><description>逐步解决 LeetCode 609：解析目录与文件描述，写出两两比较的正确基线，再按内容哈希分组，并分析路径拼接成本和真实文件系统的边界。</description></item><item><title>LeetCode 152：乘积最大子数组，从负数反例推导双状态动态规划</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/152-maximum-product-subarray/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/152-maximum-product-subarray/</guid><description>先枚举非空连续区间，再用负数反例推导最大与最小乘积状态，逐步得到常数空间的 Python 解法。</description></item><item><title>LeetCode 279：完全平方数，从剩余和推导动态规划</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/279-perfect-squares/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/279-perfect-squares/</guid><description>从最大平方数优先的失败例子出发，逐步构建递归、记忆化与迭代动态规划，求组成整数所需的最少完全平方数。</description></item><item><title>Python range() 的边界：为什么循环有时一次也不执行？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-range-empty-range/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-range-empty-range/</guid><description>从 range(10, 7) 出发，解释 Python range() 的起点、终点和步长，理解空范围为什么不会报错，以及它如何自动跳过不需要处理的循环。</description></item><item><title>Redis 任务队列系列（1）：用 Python 和 List 实现最小生产消费模型</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/redis-task-queue-python-list/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/redis-task-queue-python-list/</guid><description>使用 redis-py 和 Redis List 写出一个可以运行的最小任务队列，理解 LPUSH、BRPOP、阻塞消费、FIFO 与任务丢失边界。</description></item><item><title>后台异步任务什么时候算队列：从生产者与消费者理解 Celery、Redis</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/background-tasks-and-task-queues/</link><pubDate>Thu, 08 Oct 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/background-tasks-and-task-queues/</guid><description>从用户提交五个计算任务出发，解释后台异步执行、前端查询状态与任务队列的区别，用 Python 示例说明生产消费解耦，并讲清 Celery、Redis 和数据库队列的关系。</description></item><item><title>LeetCode 139：单词拆分，从前缀是否可达开始</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/139-word-break/</link><pubDate>Tue, 22 Sep 2026 12:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/139-word-break/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：字符串 &lt;code&gt;s&lt;/code&gt; 和字符串列表 &lt;code&gt;wordDict&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wordDict&lt;/code&gt; 中的每个元素都是一个可以使用的单词；同一个单词可以重复使用&lt;/li&gt;
&lt;li&gt;输出：如果 &lt;code&gt;s&lt;/code&gt; 可以被拆成一个或多个字典单词，返回 &lt;code&gt;True&lt;/code&gt;；否则返回 &lt;code&gt;False&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;拆分必须覆盖整个字符串，不能跳过字符，也不能改变字符顺序&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：s = &amp;#34;leetcode&amp;#34;, wordDict = [&amp;#34;leet&amp;#34;, &amp;#34;code&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：True
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：&amp;#34;leetcode&amp;#34; 可以拆成 &amp;#34;leet&amp;#34; + &amp;#34;code&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：s = &amp;#34;applepenapple&amp;#34;, wordDict = [&amp;#34;apple&amp;#34;, &amp;#34;pen&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：True
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：&amp;#34;apple&amp;#34; + &amp;#34;pen&amp;#34; + &amp;#34;apple&amp;#34;；同一个单词可以重复使用
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：s = &amp;#34;catsandog&amp;#34;, wordDict = [&amp;#34;cats&amp;#34;, &amp;#34;dog&amp;#34;, &amp;#34;sand&amp;#34;, &amp;#34;and&amp;#34;, &amp;#34;cat&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：False
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：前面可以拼出一些片段，但最后不能覆盖完整字符串
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="约束"&gt;约束&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= s.length &amp;lt;= 300&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= wordDict.length &amp;lt;= 1000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= wordDict[i].length &amp;lt;= 20&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; 和 &lt;code&gt;wordDict[i]&lt;/code&gt; 只包含小写英文字母&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wordDict&lt;/code&gt; 中的单词互不相同&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1先让已完成的前缀变得可见"&gt;Step 1：先让“已完成的前缀”变得可见&lt;/h2&gt;
&lt;p&gt;先只问一个具体问题：对于 &lt;code&gt;s = &amp;quot;leetcode&amp;quot;&lt;/code&gt;，在确认整串能否拆开之前，我们能不能先记录“已经拆完了哪一段”？&lt;/p&gt;</description></item><item><title>LeetCode 213：打家劫舍 II，环形拆成两个线性问题</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/213-house-robber-ii/</link><pubDate>Tue, 22 Sep 2026 10:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/213-house-robber-ii/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;nums&lt;/code&gt;，&lt;code&gt;nums[i]&lt;/code&gt; 表示第 &lt;code&gt;i&lt;/code&gt; 间房子的金额。&lt;/li&gt;
&lt;li&gt;房子首尾相连，不能偷相邻的两间房子，因此第 &lt;code&gt;0&lt;/code&gt; 间和第 &lt;code&gt;n - 1&lt;/code&gt; 间也不能同时偷。&lt;/li&gt;
&lt;li&gt;输出：在不触发警报的前提下，最多能偷到的金额。&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 100&lt;/code&gt;，&lt;code&gt;0 &amp;lt;= nums[i] &amp;lt;= 1000&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [2,3,2]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：第 0 间和第 2 间首尾相邻，不能同时偷；选择金额 3 的房子。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,3,1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：可以偷下标 0 和下标 2，也可以偷下标 1 和下标 3。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,3]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，先从最小的首尾冲突开始。&lt;/p&gt;
&lt;h2 id="step-1先处理首尾相邻的冲突"&gt;Step 1：先处理首尾相邻的冲突&lt;/h2&gt;
&lt;p&gt;先问一个具体问题：&lt;code&gt;nums = [2,3,2]&lt;/code&gt; 时，能不能沿用直线街道的做法，直接在整个数组上选择不相邻的房子？&lt;/p&gt;
&lt;p&gt;当前基线只有一条规则：相邻房子不能同时偷。若把这三个数当成直线，可能会把下标 &lt;code&gt;0&lt;/code&gt; 和下标 &lt;code&gt;2&lt;/code&gt; 当成彼此不相邻，从而错误地选择两端，得到金额 &lt;code&gt;4&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这在环形输入上就会失败：首尾也相邻，&lt;code&gt;0&lt;/code&gt; 和 &lt;code&gt;2&lt;/code&gt; 必须互斥。要消除这一个冲突，只增加一个分支规则：任何合法方案至少属于下面一个候选分支。&lt;/p&gt;</description></item><item><title>LeetCode 322：零钱兑换，从更小金额推出完全背包 DP</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/322-coin-change/</link><pubDate>Tue, 22 Sep 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/322-coin-change/</guid><description>从零钱兑换中的贪心失败和更小金额问题出发，逐步构建求最少硬币数量的完全背包动态规划。</description></item><item><title>LeetCode 300：最长递增子序列，从“以 i 结尾”推出序列 DP</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/300-longest-increasing-subsequence/</link><pubDate>Tue, 22 Sep 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/300-longest-increasing-subsequence/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;nums&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输出：返回 &lt;code&gt;nums&lt;/code&gt; 的最长严格递增子序列长度&lt;/li&gt;
&lt;li&gt;子序列保持原数组中的相对顺序，但不要求连续，可以跳过元素&lt;/li&gt;
&lt;li&gt;如果选择的下标是 &lt;code&gt;i_1 &amp;lt; i_2 &amp;lt; ... &amp;lt; i_k&lt;/code&gt;，还必须满足 &lt;code&gt;nums[i_1] &amp;lt; nums[i_2] &amp;lt; ... &amp;lt; nums[i_k]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;子序列非空，因此至少选择一个元素&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 2500&lt;/code&gt;，&lt;code&gt;-10^4 &amp;lt;= nums[i] &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [10,9,2,5,3,7,101,18]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：[2,5,7,101] 是一个长度为 4 的严格递增子序列；它不要求在原数组中连续。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [0,1,0,3,2,3]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：[0,1,2,3] 的长度为 4；`[0,1,3]` 也是合法但更短的选择。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从“每个位置结尾能形成多长”这个局部问题开始，逐步写出可运行解法。&lt;/p&gt;
&lt;h2 id="step-1先问以哪个位置结束"&gt;Step 1：先问“以哪个位置结束？”&lt;/h2&gt;
&lt;p&gt;先看第一个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;下标： 0 1 2 3 4 5 6 7
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;数值： 10 9 2 5 3 7 101 18
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;答案 &lt;code&gt;[2,5,7,101]&lt;/code&gt; 跳过了 &lt;code&gt;9&lt;/code&gt; 和 &lt;code&gt;3&lt;/code&gt;。当我们走到值为 &lt;code&gt;7&lt;/code&gt; 的位置时，不能只问“它左边相邻的数是谁”，而要问：&lt;/p&gt;</description></item><item><title>LeetCode 416：分割等和子集，从目标总和推到 0/1 背包倒序遍历</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/416-partition-equal-subset-sum/</link><pubDate>Tue, 22 Sep 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/416-partition-equal-subset-sum/</guid><description>从分割等和子集的题目压力出发，逐步建立 0/1 背包状态，并解释为什么一维压缩必须倒序遍历。</description></item><item><title>Python 中 @staticmethod 和 @abstractmethod 的区别</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/staticmethod-vs-abstractmethod/</link><pubDate>Wed, 09 Sep 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/staticmethod-vs-abstractmethod/</guid><description>理解 @staticmethod 与 @abstractmethod 解决的问题，掌握实例方法、静态方法、类方法和抽象方法的选择方式。</description></item><item><title>让领域模型和仓储模型各自负责</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/domain-and-repository-boundaries/</link><pubDate>Wed, 09 Sep 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/domain-and-repository-boundaries/</guid><description>用清晰的边界分开领域模型、仓储契约、ORM 模型和 API 模型，让业务规则不被数据库和接口绑架。</description></item><item><title>JSON 不只是键值对：Python 列表如何映射到 SQLAlchemy JSON 列</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/json-not-key-value-sqlalchemy-json-column/</link><pubDate>Tue, 08 Sep 2026 10:30:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/json-not-key-value-sqlalchemy-json-column/</guid><description>从一个关键词订阅的 Repository 示例出发，解释 JSON 对象与数组、Python list 与 tuple 的转换边界，以及 SQLAlchemy JSON 列的持久化和原地修改注意事项。</description></item><item><title>Python 类型标注中的 tuple[T, ...]：任意长度和固定长度</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-tuple-ellipsis-type-annotation/</link><pubDate>Tue, 08 Sep 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-tuple-ellipsis-type-annotation/</guid><description>通过 ParsedDocument 的 blocks 字段，解释 Python 中 tuple[T, ...]、tuple[T] 和 tuple[T1, T2] 的区别，以及什么时候应该使用 tuple 或 list。</description></item><item><title>Python 中 if not value 与 is None 的区别</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-if-not-vs-is-none/</link><pubDate>Tue, 08 Sep 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-if-not-vs-is-none/</guid><description>从逐行解析文档的空行过滤代码出发，解释 Python 中 if not value 与 value is None 的语义差异、适用边界和常见误区。</description></item><item><title>看懂 C# 的 Task&lt;(T1, T2)&gt;：从返回值、命名元组到 async/await</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/csharp-task-named-tuple-async-await/</link><pubDate>Tue, 01 Sep 2026 09:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/csharp-task-named-tuple-async-await/</guid><description>用一个可运行的 .NET 8 示例逐层讲清 C# 命名元组、泛型任务、异步等待、类型推断，以及如何阅读复杂的异步方法签名。</description></item><item><title>Python 字符串为什么不可变：从 replace 到变量重绑定</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-string-immutability/</link><pubDate>Fri, 28 Aug 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-string-immutability/</guid><description>用可运行示例解释 Python str 的不可变性，区分字符串对象不变与变量重新赋值，并说明 replace、切片、+= 和循环拼接的真实行为。</description></item><item><title>先做信息对等，再谈模型能力：Agent 失败时该优化系统还是模型</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/information-parity-before-model-optimization/</link><pubDate>Fri, 28 Aug 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/information-parity-before-model-optimization/</guid><description>Agent 做错判断时，不要先凭感觉升级模型或增加工作流。先冻结模型实际可见的输入，再用同输入可解性、信息增量和输出唯一性三项测试定位问题究竟在推理侧、系统侧还是任务契约。</description></item><item><title>从 Python/Linux 到 C#/.NET：为 Windows Runner 建立第一套心智模型</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/dotnet-csharp-windows-runner-primer/</link><pubDate>Wed, 26 Aug 2026 16:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/dotnet-csharp-windows-runner-primer/</guid><description>从 Windows Runner 场景入门 C# 与 .NET 8：理解 SDK、Runtime、CLR、NuGet 和 csproj，在 Ubuntu/WSL 中创建、编译并运行第一个控制台项目，并分清 Linux 开发与 Windows UKey 验收的边界。</description></item><item><title>管线只负责一次运行：文档索引系统不该默认共享跨 Run 节点</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/pipeline-is-not-cross-run-task-sharing/</link><pubDate>Wed, 26 Aug 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/pipeline-is-not-cross-run-task-sharing/</guid><description>一次 RAG 与知识图谱独立运行模式的架构讨论，让我重新分清了 Pipeline、Run、动态 Target 和跨请求 Singleflight。我的结论是：文档索引系统更适合让每次 Run 独立拥有状态和节点，同一文件只保留一个活动 Run，并通过持久化产物复用结果，而不是共享正在执行的节点任务。</description></item><item><title>Speculative Decoding 为什么能加速：从并行验证到 GPU Memory-Bound</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/speculative-decoding-gpu-memory-bound/</link><pubDate>Wed, 26 Aug 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/speculative-decoding-gpu-memory-bound/</guid><description>从自回归生成的串行依赖出发，解释 speculative decoding 如何用一次 Target forward 并行验证多个 Draft token，以及 acceptance rate、batch 和 KV Cache 如何决定实际收益。</description></item><item><title>LeetCode 155：最小栈，如何让最小值跟着栈一起变化</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/155-min-stack/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/155-min-stack/</guid><description>从弹出当前最小值后必须恢复历史最小值的压力出发，逐步构建 LeetCode 155 Min Stack。</description></item><item><title>LeetCode 20：有效的括号，为什么数量相同仍然无效</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/20-valid-parentheses/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/20-valid-parentheses/</guid><description>从反例 ([)] 出发，区分括号数量相同与括号类型、闭合顺序真正匹配，为 LeetCode 20 建立可验证的题意基础。</description></item><item><title>LeetCode 394：字符串解码，如何保存并恢复嵌套上下文</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/394-decode-string/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/394-decode-string/</guid><description>从嵌套编码会打断外层解码的压力出发，逐步构建 LeetCode 394 Decode String。</description></item><item><title>从复合意图到可执行查询：面向实体的多源证据编排</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/intent-to-evidence-compilation/</link><pubDate>Mon, 17 Aug 2026 12:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/intent-to-evidence-compilation/</guid><description>介绍一种面向实体的多源证据编排模式：将复合意图转换为语义查询计划，经实体绑定和任务展开后调用不同数据源，最后按主体组装可追溯结果。</description></item><item><title>二分查找区间怎么选：从候选集合到边界更新</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/binary-search-interval-conventions/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/binary-search-interval-conventions/</guid><description>从候选集合出发，理解二分查找的闭区间、左闭右开区间和开区间哨兵写法，以及初始化、循环条件与边界更新为什么必须配套。</description></item><item><title>LeetCode 74：搜索二维矩阵</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/74-search-a-2d-matrix/</link><pubDate>Thu, 13 Aug 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/74-search-a-2d-matrix/</guid><description>&lt;p&gt;给你一个 &lt;code&gt;m x n&lt;/code&gt; 的整数矩阵 &lt;code&gt;matrix&lt;/code&gt; 和一个整数 &lt;code&gt;target&lt;/code&gt;。如果 &lt;code&gt;target&lt;/code&gt; 在矩阵中，返回 &lt;code&gt;True&lt;/code&gt;；否则返回 &lt;code&gt;False&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;题目给出的矩阵满足两个条件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每一行都按非递减顺序排列。&lt;/li&gt;
&lt;li&gt;每一行的第一个整数都严格大于上一行的最后一个整数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要求算法的时间复杂度为 &lt;code&gt;O(log(m * n))&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;例如，对于下面的矩阵：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1 3 5 7
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;10 11 16 20
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;23 30 34 60
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;target = 3&lt;/code&gt; 时返回 &lt;code&gt;True&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;target = 13&lt;/code&gt; 时返回 &lt;code&gt;False&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;约束如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1 &amp;lt;= m, n &amp;lt;= 100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-10^4 &amp;lt;= matrix[i][j], target &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;LeetCode 提供的方法签名是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Solution&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;searchMatrix&lt;/span&gt;(self, matrix: List[List[int]], target: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; bool:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="第一步扫描每一个元素"&gt;第一步：扫描每一个元素&lt;/h2&gt;
&lt;p&gt;现在只有题目条件和方法签名，还没有一段能判断目标值是否存在的代码。先解决最基本的问题：怎样得到一个可以直接运行、结果确定正确的版本？&lt;/p&gt;
&lt;p&gt;当前基线无法对示例中的 &lt;code&gt;3&lt;/code&gt; 或 &lt;code&gt;13&lt;/code&gt; 给出答案。加入一层遍历每一行的循环，再在当前行中检查每一个值。遇到目标值就立即返回 &lt;code&gt;True&lt;/code&gt;；只有检查完所有元素仍未命中时，才返回 &lt;code&gt;False&lt;/code&gt;。&lt;/p&gt;</description></item><item><title>LeetCode 153：寻找旋转排序数组中的最小值</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/153-find-minimum-in-rotated-sorted-array/</link><pubDate>Tue, 28 Jul 2026 16:13:57 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/153-find-minimum-in-rotated-sorted-array/</guid><description>从线性扫描基线推导包含最小值的闭区间与二分更新，在 O(log n) 时间、O(1) 额外空间内返回旋转数组的最小值。</description></item><item><title>LeetCode 33：搜索旋转排序数组</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/33-search-in-rotated-sorted-array/</link><pubDate>Tue, 28 Jul 2026 15:40:34 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/33-search-in-rotated-sorted-array/</guid><description>从线性扫描基线推导旋转数组中的有序半区与目标范围判断，在 O(log n) 时间、O(1) 额外空间内完成搜索。</description></item><item><title>LeetCode 503：下一个更大元素 II，循环数组中的右侧到哪里结束</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/503-next-greater-element-ii/</link><pubDate>Mon, 20 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/503-next-greater-element-ii/</guid><description>从循环数组的严格下一个更大元素定义出发，先写可验证的环形扫描，再推导只压入一次原始索引的线性单调栈解法。</description></item><item><title>LeetCode 84：柱状图中最大的矩形，连续区间的高度由谁决定</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/84-largest-rectangle-in-histogram/</link><pubDate>Mon, 20 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/84-largest-rectangle-in-histogram/</guid><description>从连续区间的最矮柱限制出发，先构造枚举区间 baseline，再推导按柱高结算最大宽度的线性单调栈解法。</description></item><item><title>LeetCode 136：只出现一次的数字，如何在常量空间排除重复元素</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/136-single-number/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/136-single-number/</guid><description>从集合配对 baseline 出发，推导 XOR 抵消重复元素，在线性时间和常量额外空间内解决 LeetCode 136。</description></item><item><title>LeetCode 191：位 1 的个数，如何跳过无关的零位</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/191-number-of-1-bits/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/191-number-of-1-bits/</guid><description>从逐位右移 baseline 出发，推导 n &amp;amp; (n - 1) 每次清除最低位的一个 1，并用循环 invariant 证明 LeetCode 191。</description></item><item><title>LeetCode 338：比特位计数，如何复用较小数字的结果</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/338-counting-bits/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/338-counting-bits/</guid><description>从对 0..n 每个数字重复统计的 baseline 出发，推导 answer[i] = answer[i &amp;amp; (i - 1)] + 1，在线性时间内完成比特位计数。</description></item><item><title>LeetCode 739：每日温度，如何找到右侧第一个更高温度</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/739-daily-temperatures/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/739-daily-temperatures/</guid><description>从向右扫描的 O(n²) baseline 出发，推导维护未解决日期的单调索引栈，在线性时间内解决 LeetCode 739 每日温度。</description></item><item><title>LeetCode 45：跳跃游戏 II，从可达边界升级到最少跳数</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/45-jump-game-ii/</link><pubDate>Wed, 08 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/45-jump-game-ii/</guid><description>从 LeetCode 55 的 farthest 可达边界出发，推导 LeetCode 45 跳跃游戏 II：用 current_end 表示当前跳数覆盖边界，用 farthest 表示下一跳最远覆盖，最终得到最少跳数贪心。</description></item><item><title>LeetCode 435：无重叠区间，从删除最少转成保留最多</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/435-non-overlapping-intervals/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/435-non-overlapping-intervals/</guid><description>从删除哪个区间的选择压力出发，推导 LeetCode 435 无重叠区间：先把最少删除转换成最多保留，再逐步推出按结束位置排序的区间贪心。</description></item><item><title>LeetCode 452：用最少数量的箭引爆气球，从共同交集推出区间贪心</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/452-minimum-number-of-arrows-to-burst-balloons/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/452-minimum-number-of-arrows-to-burst-balloons/</guid><description>从一支箭能覆盖哪些气球出发，推导 LeetCode 452 用最少数量的箭引爆气球：先写共同交集扫描基线，再压缩成按结束位置排序的 arrow_pos 贪心。</description></item><item><title>LeetCode 121：买卖股票的最佳时机，从历史最低价推出一次交易贪心</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/121-best-time-to-buy-and-sell-stock/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/121-best-time-to-buy-and-sell-stock/</guid><description>从买入必须早于卖出的顺序压力出发，推导 LeetCode 121 买卖股票的最佳时机：先写 O(n^2) 正确基线，再压缩成维护历史最低价的一次扫描贪心。</description></item><item><title>LeetCode 55：跳跃游戏，用最远覆盖范围判断能否到达终点</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/55-jump-game/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/greedy/55-jump-game/</guid><description>从会卡在 0 的失败样例出发，推导 LeetCode 55 跳跃游戏：先用 reachable 数组建立正确基线，再压缩成维护 farthest 最远覆盖范围的贪心。</description></item><item><title>LeetCode 200：岛屿数量，把网格陆地看成连通分量</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/union-find/200-number-of-islands/</link><pubDate>Thu, 02 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/union-find/200-number-of-islands/</guid><description>从网格中相邻陆地的合并压力出发，用并查集推导 LeetCode 200 岛屿数量，讲清二维坐标映射、陆地初始 count、union 后 count 什么时候减少。</description></item><item><title>LeetCode 684：冗余连接，用 union 失败找到成环边</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/union-find/684-redundant-connection/</link><pubDate>Thu, 02 Jul 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/union-find/684-redundant-connection/</guid><description>从树加一条边必然成环的压力出发，用并查集推导 LeetCode 684 冗余连接，讲清 1-indexed parent、union 返回值和为什么 union 失败就是答案。</description></item><item><title>并查集模板：find / union / count 从零推导</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/union-find/union-find-template/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/union-find/union-find-template/</guid><description>从一个集合合并小任务出发，推导并查集模板中的 parent、find、union、count 和路径压缩，最终得到可直接背写的 Python 模板。</description></item><item><title>LeetCode 208：实现 Trie（前缀树）模板题解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/trie/208-implement-trie-prefix-tree/</link><pubDate>Wed, 24 Jun 2026 17:38:48 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/trie/208-implement-trie-prefix-tree/</guid><description>LeetCode 208 Implement Trie 是 Trie 模板题，核心是按固定接口实现 insert、search 和 startsWith。</description></item><item><title>Trie 模板：从节点字段到插入查询 invariant</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/trie/trie-template/</link><pubDate>Wed, 24 Jun 2026 17:38:48 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/trie/trie-template/</guid><description>从零搭一个 Trie 模板，重点理解每个节点存什么、children 怎么走、is_end 为什么需要，以及 insert/search 的循环 invariant。</description></item><item><title>Python Callable 类型标注：怎么描述一个 handler 函数？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-callable-type-alias-workflow-handler/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-callable-type-alias-workflow-handler/</guid><description>&lt;h1 id="python-callable-类型标注怎么描述一个-handler-函数"&gt;Python Callable 类型标注：怎么描述一个 handler 函数？&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题：&lt;/strong&gt; &lt;code&gt;Callable[[dict[str, Any]], Any]&lt;/code&gt; 不是一个新类，而是在类型层面说明：
这个参数是一个“能被调用的对象”，它接收什么参数，返回什么结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;标签：&lt;/strong&gt; Python / 类型标注 / Callable / Type Alias / Pipeline
&lt;strong&gt;适读人群：&lt;/strong&gt; 正在写回调、handler、pipeline step、插件机制的 Python 开发者
&lt;strong&gt;阅读时间：&lt;/strong&gt; 6 min&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="背景为什么-handler-类型看起来这么长"&gt;背景：为什么 handler 类型看起来这么长？&lt;/h2&gt;
&lt;p&gt;在 pipeline runner 里，经常会有这种 handler 表：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;handlers &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;prepare&amp;#34;&lt;/span&gt;: prepare,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;enrich&amp;#34;&lt;/span&gt;: enrich,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个 handler 都接收同一个运行时上下文：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;prepare&lt;/span&gt;(context: dict[str, Any]) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; dict[str, bool]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; context[&lt;span style="color:#e6db74"&gt;&amp;#34;prepared&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;prepared&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果直接给 handler 表写类型，会变成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; collections.abc &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Callable, Mapping
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Any
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;handlers: Mapping[str, Callable[[dict[str, Any]], Any]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这一串不难，但读起来吵。它把“workflow name 到 workflow handler 的映射”写成了一段类型语法。&lt;/p&gt;</description></item><item><title>Python 从 dict/list 原型到稳定接口：什么时候该整理类型？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-prototype-to-typed-interface/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-prototype-to-typed-interface/</guid><description>&lt;h1 id="python-从-dictlist-原型到稳定接口什么时候该整理类型"&gt;Python 从 dict/list 原型到稳定接口：什么时候该整理类型？&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题：&lt;/strong&gt; 验证想法时先用普通变量和 &lt;code&gt;dict/list&lt;/code&gt; 跑通行为；模型稳定后，再把它们整理成函数参数、dataclass、helper 和类型标注。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;标签：&lt;/strong&gt; Python / 原型验证 / 类型标注 / 接口设计 / Pipeline
&lt;strong&gt;适读人群：&lt;/strong&gt; 正在从脚本式代码过渡到可维护模块的 Python 开发者
&lt;strong&gt;阅读时间：&lt;/strong&gt; 7 min&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="背景为什么一开始不用把类型设计得很完整"&gt;背景：为什么一开始不用把类型设计得很完整？&lt;/h2&gt;
&lt;p&gt;写一个新功能时，经常会卡在这个问题上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;我应该一开始就写 dataclass、class、Mapping、Callable 吗？
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;还是先用普通 dict/list 把行为跑通？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;答案不是固定的，但有一个很实用的判断：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;模型还不确定时，先用 dict/list 验证行为；
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;模型稳定后，再把稳定的东西整理成接口。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这不是偷懒，而是在降低错误抽象的成本。&lt;/p&gt;
&lt;p&gt;比如你想验证一个 graph pipeline runner，最早可以只写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;graph_plans &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;parallel&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;prepare&amp;#34;&lt;/span&gt;: (),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;enrich&amp;#34;&lt;/span&gt;: (&lt;span style="color:#e6db74"&gt;&amp;#34;prepare&amp;#34;&lt;/span&gt;,),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;extract&amp;#34;&lt;/span&gt;: (&lt;span style="color:#e6db74"&gt;&amp;#34;prepare&amp;#34;&lt;/span&gt;,),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个变量一开始不是“最终架构”，只是为了回答一个问题：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dependency map 能不能表达 prepare 之后 enrich 和 extract 同时 ready？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果这个问题都没验证清楚，提前设计完整类层次反而会遮住真正的行为压力。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="第一阶段用普通变量验证模型"&gt;第一阶段：用普通变量验证模型&lt;/h2&gt;
&lt;p&gt;最早的脚本里，很多东西看起来像“常量”：&lt;/p&gt;</description></item><item><title>Python 类型标注里为什么用 Mapping、Sequence 这类抽象接口？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-mapping-for-readonly-config/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-mapping-for-readonly-config/</guid><description>&lt;h1 id="python-类型标注里为什么用-mappingsequence-这类抽象接口"&gt;Python 类型标注里为什么用 Mapping、Sequence 这类抽象接口？&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题：&lt;/strong&gt; 当一个函数只读取配置，不修改调用方传入的字典时，用
&lt;code&gt;Mapping&lt;/code&gt; 比直接写 &lt;code&gt;dict&lt;/code&gt; 更能表达接口意图；同理，&lt;code&gt;Iterable&lt;/code&gt;、&lt;code&gt;Sequence&lt;/code&gt;
和 &lt;code&gt;Callable&lt;/code&gt; 也在表达“我需要什么能力”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;标签：&lt;/strong&gt; Python / 类型标注 / Mapping / Sequence / Callable / 接口设计
&lt;strong&gt;适读人群：&lt;/strong&gt; 正在写 Python 配置对象、runner、pipeline、SDK 参数的开发者
&lt;strong&gt;阅读时间：&lt;/strong&gt; 7 min&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="背景为什么不是所有字典参数都写成-dict"&gt;背景：为什么不是所有字典参数都写成 dict？&lt;/h2&gt;
&lt;p&gt;写 Python 配置型代码时，经常会有这种参数：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;graph_plans &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;parallel&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;prepare&amp;#34;&lt;/span&gt;: (),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;enrich&amp;#34;&lt;/span&gt;: (&lt;span style="color:#e6db74"&gt;&amp;#34;prepare&amp;#34;&lt;/span&gt;,),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;extract&amp;#34;&lt;/span&gt;: (&lt;span style="color:#e6db74"&gt;&amp;#34;prepare&amp;#34;&lt;/span&gt;,),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果一个 runner 只是读取它：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;runner &lt;span style="color:#f92672"&gt;=&lt;/span&gt; GraphPipelineRunner(graph_plans&lt;span style="color:#f92672"&gt;=&lt;/span&gt;graph_plans, handlers&lt;span style="color:#f92672"&gt;=&lt;/span&gt;handlers)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;那么类型标注可以写成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; collections.abc &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Mapping
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; graph_plans: Mapping[str, Mapping[str, tuple[str, &lt;span style="color:#f92672"&gt;...&lt;/span&gt;]]],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handlers: Mapping[str, WorkflowHandler],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;刚开始看会觉得复杂：为什么不直接写 &lt;code&gt;dict&lt;/code&gt;？&lt;/p&gt;</description></item><item><title>函数命名不是取名字：它是在描述系统边界</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/function-naming-design-intent/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/function-naming-design-intent/</guid><description>用一个小型 graph pipeline runner 解释 get、resolve、validate、ready、skip 等函数命名如何表达行为、失败语义和副作用。</description></item><item><title>从顺序 Pipeline Runner 演进到 Graph Runner</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/minimal-pipeline-runner-v1/</link><pubDate>Wed, 03 Jun 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/minimal-pipeline-runner-v1/</guid><description>在最小顺序 Pipeline Runner 的基础上，继续推导 graph pipeline runner：为什么 workflow list 不够用，为什么要用 dependency map、ready wave、GraphNodeRecord 和失败分支跳过。</description></item><item><title>为什么开会，以及怎么开一个有效会议</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/why-and-how-to-run-meetings/</link><pubDate>Sun, 31 May 2026 21:30:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/why-and-how-to-run-meetings/</guid><description>从目标对齐、决策、问题暴露、行动分配和会议记录出发，说明什么时候需要开会，以及怎样让会议产出明确结果。</description></item><item><title>从普通函数调用推导一个最小 Pipeline Runner</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/minimal-pipeline-runner-v0/</link><pubDate>Fri, 29 May 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/minimal-pipeline-runner-v0/</guid><description>从普通函数调用开始，逐步推导一个最小顺序 Pipeline Runner：工程接入场景、run/step observation、共享 context、mode 解析、handler 表和 PipelineRunner.trigger。</description></item><item><title>LeetCode 547：省份数量，把邻接矩阵看成图的连通分量</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/547-number-of-provinces/</link><pubDate>Wed, 27 May 2026 00:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/547-number-of-provinces/</guid><description>从邻接矩阵的直接连接关系出发，把 LeetCode 547 省份数量转成无向图连通分量计数问题，再逐步推出 DFS、BFS 和并查集解法。</description></item><item><title>为什么 AI 在代码领域最先自动化</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/ai-automation-needs-artifact-workflow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/ai-automation-needs-artifact-workflow/</guid><description>代码领域之所以最早得到 AI 自动化，不只是因为代码适合大模型，而是因为软件工程已经拥有版本控制、测试、增量提交和回滚机制。其他领域并不是没有评价标准，而是缺少把标准绑定到中间版本上的工作流。文章、小说、视频、3D 模型等复杂作品也能被拆解、验证、冻结和追溯；而且文字领域天然拥有经典作品、评论和写作理论作为 reference corpus。一旦这些参照物被转成任务、rubric 和 checkpoint，AI 可能比在代码领域释放出更强的能力。</description></item><item><title>Docker 容器如何使用宿主机 Clash 代理</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/docker-container-use-host-clash-proxy/</link><pubDate>Fri, 15 May 2026 23:50:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/docker-container-use-host-clash-proxy/</guid><description>记录一次 Docker 容器无法直连 OpenAI OAuth 的排障过程，并说明如何通过 host.docker.internal 复用宿主机 Clash 代理。</description></item><item><title>LeetCode 198：打家劫舍，从偷或不偷推出一维 DP</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/198-house-robber/</link><pubDate>Sun, 03 May 2026 14:33:39 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/198-house-robber/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;nums&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums[i]&lt;/code&gt; 表示第 &lt;code&gt;i&lt;/code&gt; 间房子的金额&lt;/li&gt;
&lt;li&gt;不能偷相邻房子&lt;/li&gt;
&lt;li&gt;输出：返回最多能偷到的金额&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 100&lt;/code&gt;，&lt;code&gt;0 &amp;lt;= nums[i] &amp;lt;= 400&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,3,1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：偷下标 0 和下标 2，金额 1 + 3 = 4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [2,7,9,3,1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：12
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：偷下标 0、2、4，金额 2 + 9 + 1 = 12
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从这个二选一冲突推出一维 DP。&lt;/p&gt;
&lt;h2 id="从-1231-的相邻冲突开始"&gt;从 &lt;code&gt;[1,2,3,1]&lt;/code&gt; 的相邻冲突开始&lt;/h2&gt;
&lt;p&gt;看例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nums = [1,2,3,1]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果偷下标 &lt;code&gt;2&lt;/code&gt; 的房子，金额是 &lt;code&gt;3&lt;/code&gt;，那么下标 &lt;code&gt;1&lt;/code&gt; 和下标 &lt;code&gt;3&lt;/code&gt; 都不能偷。
如果不偷下标 &lt;code&gt;2&lt;/code&gt;，答案可能来自前面下标 &lt;code&gt;0..1&lt;/code&gt; 的最优结果。&lt;/p&gt;
&lt;p&gt;所以走到某一间房时，核心选择只有两个：&lt;/p&gt;</description></item><item><title>LeetCode 70：爬楼梯，从 dp[i] 含义推出一维 DP</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/70-climbing-stairs/</link><pubDate>Sun, 03 May 2026 14:33:39 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/70-climbing-stairs/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数 &lt;code&gt;n&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;含义：爬到第 &lt;code&gt;n&lt;/code&gt; 阶楼顶&lt;/li&gt;
&lt;li&gt;每次可以爬 &lt;code&gt;1&lt;/code&gt; 或 &lt;code&gt;2&lt;/code&gt; 阶&lt;/li&gt;
&lt;li&gt;输出：返回到达楼顶的不同走法数量&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= n &amp;lt;= 45&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：n = 2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：1+1，2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：n = 3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：1+1+1，1+2，2+1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从 &lt;code&gt;dp[i]&lt;/code&gt; 的含义一步一步推出最终代码。&lt;/p&gt;
&lt;h2 id="从-n--3-的最后一步开始"&gt;从 &lt;code&gt;n = 3&lt;/code&gt; 的最后一步开始&lt;/h2&gt;
&lt;p&gt;先看最小能暴露转移的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;n = 3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;到第 3 阶的最后一步只可能来自：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第 2 阶，再走 1 阶&lt;/li&gt;
&lt;li&gt;第 1 阶，再走 2 阶&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以“到第 3 阶的方法数”不是凭空算出来的，而是来自两个更小的位置。&lt;/p&gt;
&lt;h2 id="step-1先定义更小的问题"&gt;Step 1：先定义更小的问题&lt;/h2&gt;
&lt;p&gt;直接问“到第 &lt;code&gt;n&lt;/code&gt; 阶有几种走法”太大。先定义：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dp[i] = 到达第 i 阶的方法数
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意这里的 &lt;code&gt;i&lt;/code&gt; 是楼梯位置，不是数组下标含义上的第几个元素。&lt;/p&gt;</description></item><item><title>LeetCode 746：使用最小花费爬楼梯，从 top 位置推出 dp</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/746-min-cost-climbing-stairs/</link><pubDate>Sun, 03 May 2026 14:33:39 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/746-min-cost-climbing-stairs/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;cost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cost[i]&lt;/code&gt; 表示踩到第 &lt;code&gt;i&lt;/code&gt; 阶的代价&lt;/li&gt;
&lt;li&gt;每次可以爬 &lt;code&gt;1&lt;/code&gt; 或 &lt;code&gt;2&lt;/code&gt; 阶&lt;/li&gt;
&lt;li&gt;可以从下标 &lt;code&gt;0&lt;/code&gt; 或下标 &lt;code&gt;1&lt;/code&gt; 开始&lt;/li&gt;
&lt;li&gt;输出：返回到达楼顶的最小花费&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;2 &amp;lt;= cost.length &amp;lt;= 1000&lt;/code&gt;，&lt;code&gt;0 &amp;lt;= cost[i] &amp;lt;= 999&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：cost = [10,15,20]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：15
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：从下标 1 开始，付 15 后直接到达 top
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：cost = [1,100,1,1,1,100,1,1,100,1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="从-101520-的-top-位置开始"&gt;从 &lt;code&gt;[10,15,20]&lt;/code&gt; 的 top 位置开始&lt;/h2&gt;
&lt;p&gt;先看最小例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cost = [10,15,20]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;楼顶不是下标 &lt;code&gt;2&lt;/code&gt;，而是在最后一阶之后的位置，可以记为位置 &lt;code&gt;3&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;到达 top 位置 &lt;code&gt;3&lt;/code&gt; 的最后一步只可能来自：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;位置 &lt;code&gt;2&lt;/code&gt;，付 &lt;code&gt;cost[2]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;位置 &lt;code&gt;1&lt;/code&gt;，付 &lt;code&gt;cost[1]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这题最容易错的地方就在这里：我们要求的是“到达 top 的花费”，不是“到达最后一个下标的花费”。&lt;/p&gt;</description></item><item><title>LeetCode 53：最大子数组和，从 dp[i] 含义推出 Kadane</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/53-maximum-subarray/</link><pubDate>Sun, 03 May 2026 14:25:07 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/53-maximum-subarray/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：整数数组 &lt;code&gt;nums&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输出：返回连续非空子数组的最大和&lt;/li&gt;
&lt;li&gt;子数组必须连续&lt;/li&gt;
&lt;li&gt;至少选一个元素&lt;/li&gt;
&lt;li&gt;约束：&lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10^5&lt;/code&gt;，&lt;code&gt;-10^4 &amp;lt;= nums[i] &amp;lt;= 10^4&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [-2,1,-3,4,-1,2,1,-5,4]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：6
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;解释：[4,-1,2,1] 的和最大，为 6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从 &lt;code&gt;dp[i]&lt;/code&gt; 的含义一步一步推出 Kadane 算法。&lt;/p&gt;
&lt;h2 id="从--21-34-的断点开始"&gt;从 &lt;code&gt;[-2,1,-3,4]&lt;/code&gt; 的断点开始&lt;/h2&gt;
&lt;p&gt;看一个前缀例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nums = [-2, 1, -3, 4]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果走到 &lt;code&gt;4&lt;/code&gt;，有两种选择：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把前面的某个连续子数组接到 &lt;code&gt;4&lt;/code&gt; 前面&lt;/li&gt;
&lt;li&gt;从 &lt;code&gt;4&lt;/code&gt; 自己重新开始&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里的关键不是“所有子数组怎么枚举”，而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当我们决定一个最优子数组必须以当前位置结尾时，它的来源只有两种：接上前一个位置的最优结尾，或者从当前位置重新开始。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是一维 DP 的入口。&lt;/p&gt;
&lt;h2 id="step-1先定义一个更小的问题"&gt;Step 1：先定义一个更小的问题&lt;/h2&gt;
&lt;p&gt;直接问“整个数组的最大子数组和是多少”太大。我们先强加一个限制：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果子数组必须以第 &lt;code&gt;i&lt;/code&gt; 个元素结尾，它的最大和是多少？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个值记为 &lt;code&gt;dp[i]&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这里的 &lt;code&gt;dp[i]&lt;/code&gt; 不是“前 &lt;code&gt;i&lt;/code&gt; 个元素里的最大子数组和”。它更窄：&lt;/p&gt;</description></item><item><title>LeetCode 78：子集，从搜索树推出 startIndex 回溯模板</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/78-subsets/</link><pubDate>Sun, 03 May 2026 14:25:07 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/78-subsets/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：&lt;code&gt;nums&lt;/code&gt;，长度 &lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;元素范围：&lt;code&gt;-10 &amp;lt;= nums[i] &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;所有元素互不相同&lt;/li&gt;
&lt;li&gt;输出：返回 &lt;code&gt;nums&lt;/code&gt; 的所有可能子集&lt;/li&gt;
&lt;li&gt;顺序：结果顺序不限，子集内部不需要考虑排列顺序&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,3]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python 构造一个最小可运行解法。&lt;/p&gt;
&lt;h2 id="从-12-的搜索树开始"&gt;从 &lt;code&gt;[1,2]&lt;/code&gt; 的搜索树开始&lt;/h2&gt;
&lt;p&gt;最小但有分叉的例子是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nums = [1,2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;答案应该是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[], [1], [1,2], [2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意这里没有 &lt;code&gt;[2,1]&lt;/code&gt;。这说明问题不是“随便排列所有选择”，而是“每个元素选或不选，且同一组元素只出现一次”。&lt;/p&gt;
&lt;p&gt;如果把构造过程画成树：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;|- [1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| |- [1,2]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;|- [2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这棵树暴露出两个事实：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个节点都是一个合法子集。&lt;/li&gt;
&lt;li&gt;进入 &lt;code&gt;[1]&lt;/code&gt; 之后，后面只能继续看 &lt;code&gt;2&lt;/code&gt;，不能回头再选 &lt;code&gt;1&lt;/code&gt; 或生成 &lt;code&gt;[2,1]&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1先说清楚一个递归层在解决什么"&gt;Step 1：先说清楚一个递归层在解决什么&lt;/h2&gt;
&lt;p&gt;如果当前已经选出一个 &lt;code&gt;path&lt;/code&gt;，剩下的问题是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从某个起点之后的元素里，继续选择若干个元素，扩展当前 &lt;code&gt;path&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个“某个起点”必须被记下来。否则 &lt;code&gt;[1,2]&lt;/code&gt; 和 &lt;code&gt;[2,1]&lt;/code&gt; 会从不同路径重复出现。&lt;/p&gt;
&lt;p&gt;先写最小骨架。它只保留两个状态：&lt;/p&gt;</description></item><item><title>LeetCode 90：子集 II，从重复分支推出层内去重</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/90-subsets-ii/</link><pubDate>Sun, 03 May 2026 14:25:07 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/90-subsets-ii/</guid><description>&lt;h2 id="题目要求"&gt;题目要求&lt;/h2&gt;
&lt;h3 id="输入输出"&gt;输入输出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;输入：&lt;code&gt;nums&lt;/code&gt;，长度 &lt;code&gt;1 &amp;lt;= nums.length &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;元素范围：&lt;code&gt;-10 &amp;lt;= nums[i] &amp;lt;= 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nums&lt;/code&gt; 可能有重复元素&lt;/li&gt;
&lt;li&gt;输出：返回所有不重复子集&lt;/li&gt;
&lt;li&gt;顺序：结果顺序不限，但同一个值序列的子集只能出现一次&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="示例"&gt;示例&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输入：nums = [1,2,2]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;输出：[[],[1],[1,2],[1,2,2],[2],[2,2]]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这篇只用 Python，从一个能跑但浪费的版本，一步一步过渡到排序 + 层内去重。&lt;/p&gt;
&lt;h2 id="从-122-的重复分支开始"&gt;从 &lt;code&gt;[1,2,2]&lt;/code&gt; 的重复分支开始&lt;/h2&gt;
&lt;p&gt;最小能暴露问题的例子是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nums = [1,2,2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果直接照搬 &lt;code&gt;78. 子集&lt;/code&gt; 的模板，搜索树里会出现两条不同分支：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;选择下标 1 的 2 -&amp;gt; [2]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;选择下标 2 的 2 -&amp;gt; [2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这两个分支路径不同，但值序列相同，最终答案重复。&lt;/p&gt;
&lt;p&gt;所以这题真正新增的问题不是“会不会回溯”，而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;怎样跳过重复分支，同时保留 &lt;code&gt;[2,2]&lt;/code&gt; 这种合法答案？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-1先复用-78-的状态看看哪里会坏"&gt;Step 1：先复用 78 的状态，看看哪里会坏&lt;/h2&gt;
&lt;p&gt;当前部分答案仍然需要 &lt;code&gt;path&lt;/code&gt;；当前层仍然需要 &lt;code&gt;start&lt;/code&gt; 控制只能往右选。&lt;/p&gt;
&lt;p&gt;先写出 78 的核心版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dfs&lt;/span&gt;(start: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;copy())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(start, len(nums)):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(nums[i])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dfs(i &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; path&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pop()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个版本在 &lt;code&gt;nums&lt;/code&gt; 全部互不相同时是正确的。&lt;/p&gt;</description></item><item><title>Attention-Based Seq2Seq：为什么会自然过渡到 Transformer</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/attention-based-seq2seq-to-transformer/</link><pubDate>Thu, 23 Apr 2026 15:51:40 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/attention-based-seq2seq-to-transformer/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;
这篇文章专门解释一个关键过渡：为什么 fixed-length 的 seq2seq 很快会不够用，attention-based seq2seq 是怎么补上“按需读取源序列”这个能力的，以及这个思路为什么几乎会自然长成 Transformer。最后会收束到一份最小可运行的 PyTorch GRU + additive attention 实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="从苹果为什么老翻不准开始"&gt;从“苹果”为什么老翻不准开始&lt;/h2&gt;
&lt;p&gt;还是用这个最小翻译任务：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;src: &amp;lt;bos&amp;gt; I really love green apples &amp;lt;eos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tgt: &amp;lt;bos&amp;gt; 我 真的 喜欢 青 苹果 &amp;lt;eos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当 decoder 走到要生成“苹果”这一步时，最理想的行为其实很明确：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它应该重点回头看源序列里的 &lt;code&gt;apples&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;它可能顺手也看一眼 &lt;code&gt;green&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;它不能只依赖一个已经被反复压缩过很多轮的最终隐藏状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你用上一篇里那个最小 seq2seq：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;encoder 把整句读完&lt;/li&gt;
&lt;li&gt;只把最后一个 &lt;code&gt;hidden_enc&lt;/code&gt; 交给 decoder&lt;/li&gt;
&lt;li&gt;decoder 后面每一步都只靠这个固定长度状态和自己的历史&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么句子一长，这里就会出现一个很具体的问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;decoder 明明需要“现在按需去看源序列的某几个位置”，但 fixed-length seq2seq 只给了它“一次性打包好的整句摘要”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是 attention-based seq2seq 出现的真实压力。
它不是为了“概念更高级”，而是因为 decoder 在每个时间步都需要&lt;strong&gt;重新决定自己该看源序列的哪里&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="快速掌握地图"&gt;快速掌握地图&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;fixed-length seq2seq：&lt;code&gt;encoder outputs -&amp;gt; 丢弃大部分，只保留 final hidden&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;attention-based seq2seq：&lt;code&gt;decoder step t -&amp;gt; 对所有 encoder outputs 打分 -&amp;gt; 加权求和得到 context_t&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;核心收益：不同目标位置可以读取不同源位置&lt;/li&gt;
&lt;li&gt;仍然存在的限制：encoder 和 decoder 还是循环结构，时间上依然串行&lt;/li&gt;
&lt;li&gt;通向 Transformer 的关键桥：&lt;code&gt;decoder state 作为 query，encoder outputs 作为 memory&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="这篇文章重点深挖的两个概念"&gt;这篇文章重点深挖的两个概念&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;对齐分数与上下文向量&lt;/strong&gt;：decoder 怎样在每一步决定“该看源序列哪里”&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;从 attention-based seq2seq 到 Transformer 的结构映射&lt;/strong&gt;：哪些东西被保留了，哪些东西被替换了&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="大师级心智模型"&gt;大师级心智模型&lt;/h2&gt;
&lt;p&gt;fixed-length seq2seq 的核心假设是：&lt;/p&gt;</description></item><item><title>Seq2Seq 与 Encoder-Decoder：从翻译任务到最小可运行 PyTorch 实现</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/seq2seq-encoder-decoder-pytorch-minimal/</link><pubDate>Thu, 23 Apr 2026 15:27:55 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/seq2seq-encoder-decoder-pytorch-minimal/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;
这篇文章不把 seq2seq 和 encoder-decoder 当成术语表来讲，而是从一个最小翻译任务出发，解释为什么“输入一段序列、输出另一段序列”会自然逼出编码器和解码器的分工，最后收束成一份最小可运行的 PyTorch GRU 实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="从一个最小翻译任务开始"&gt;从一个最小翻译任务开始&lt;/h2&gt;
&lt;p&gt;假设源序列是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;bos&amp;gt; I love apples &amp;lt;eos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;目标序列是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;bos&amp;gt; 我 喜欢 苹果 &amp;lt;eos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当模型要生成“苹果”时，它至少要解决三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它必须知道整句英文大意，而不只是当前一个词&lt;/li&gt;
&lt;li&gt;它必须记住自己前面已经生成了“我 喜欢”&lt;/li&gt;
&lt;li&gt;它必须按顺序一个词一个词地产生输出，而不是一次吐出整个目标序列&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你只用一个普通分类器把源句子映射成一个类别，这个任务做不成。
因为这里的输出不是一个固定标签，而是长度可变的目标序列。&lt;/p&gt;
&lt;p&gt;所以这里天然会逼出一个更具体的数据流：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先把源序列读完，压成可传递的状态&lt;/li&gt;
&lt;li&gt;再从 &lt;code&gt;&amp;lt;bos&amp;gt;&lt;/code&gt; 开始，逐步生成目标序列&lt;/li&gt;
&lt;li&gt;每生成一步，都要同时依赖“源侧信息”和“目标侧历史”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这就是 &lt;code&gt;sequence-to-sequence&lt;/code&gt; 的最小问题形态。
&lt;code&gt;seq2seq&lt;/code&gt; 说的是任务：输入一段序列，输出另一段序列。
&lt;code&gt;encoder-decoder&lt;/code&gt; 说的是实现：先编码输入，再逐步解码输出。&lt;/p&gt;
&lt;p&gt;下面不先堆名词，直接按这个压力把代码一步一步长出来。&lt;/p&gt;
&lt;h2 id="快速掌握地图"&gt;快速掌握地图&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;问题形态：&lt;code&gt;src -&amp;gt; encoder -&amp;gt; hidden/context -&amp;gt; decoder -&amp;gt; logits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;核心目标：学习条件概率 &lt;code&gt;p(y_t | y_{&amp;lt;t}, x)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;最小实现：&lt;code&gt;Embedding + GRU Encoder + GRU Decoder + output projection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;何时适用：翻译、摘要、改写、问答这类“输入序列 -&amp;gt; 输出序列”任务&lt;/li&gt;
&lt;li&gt;明显局限：如果所有源信息都被压进一个固定长度向量，长句子会吃力&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="这篇文章重点深挖的两个概念"&gt;这篇文章重点深挖的两个概念&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;隐藏状态交接&lt;/strong&gt;：encoder 到底把什么交给 decoder&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;右移目标序列与 teacher forcing&lt;/strong&gt;：训练时 decoder 为什么不能直接喂完整真实答案&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="大师级心智模型"&gt;大师级心智模型&lt;/h2&gt;
&lt;p&gt;这类模型的核心抽象不是“两个 RNN 拼起来”，而是：&lt;/p&gt;</description></item><item><title>Transformer 结构推导：一步一步搭出最小可运行 PyTorch 实现</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/transformer-architecture-overview/</link><pubDate>Thu, 23 Apr 2026 10:13:23 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/transformer-architecture-overview/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;
这篇文章不把 Transformer 当成一个现成黑盒来介绍，而是直接从一个最小翻译任务开始，让需要的结构一层一层长出来，最后收束成一份最小可运行的 PyTorch encoder-decoder Transformer。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="从一个最小翻译任务开始"&gt;从一个最小翻译任务开始&lt;/h2&gt;
&lt;p&gt;假设源序列是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;bos&amp;gt; I love apples &amp;lt;eos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;目标序列是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;bos&amp;gt; 我 喜欢 苹果
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当模型要生成“苹果”时：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它不能看目标序列里未来还没生成的位置&lt;/li&gt;
&lt;li&gt;它需要重点读取源序列中的 &lt;code&gt;apples&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;它可能还需要参考前面的“我 喜欢”来决定当前词&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以这里天然会逼出三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;目标侧必须有&lt;strong&gt;因果约束&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;源侧和目标侧都需要&lt;strong&gt;全局读取&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;解码器不仅要读自己，还要读编码器输出&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;RNN 和 CNN 也能处理序列，但它们在长距离依赖和全并行训练上都有明显限制。
所以这里真正要解决的，不只是“做一个更深的网络”，而是让任意位置能直接交互，并且显式控制信息流方向。&lt;/p&gt;
&lt;p&gt;下面开始按这个压力一步一步长代码。&lt;/p&gt;
&lt;h2 id="step-1先有输入表示但先不谈注意力"&gt;Step 1：先有输入表示，但先不谈注意力&lt;/h2&gt;
&lt;p&gt;先看一个已经分词并编号后的最小输入：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;bos&amp;gt; I love apples &amp;lt;eos&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-&amp;gt; [1, 15, 982, 204, 2]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的 &lt;code&gt;1, 15, 982, 204, 2&lt;/code&gt; 还不是模型已经理解后的表示。
它们只是词表里的编号，作用更接近“标签”或“学号”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;15&lt;/code&gt; 比 &lt;code&gt;204&lt;/code&gt; 小，不代表它们语义上更接近&lt;/li&gt;
&lt;li&gt;直接拿这种离散编号去做线性变换或点积，含义也不对&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以第一步只解决一个更具体的问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;怎样把“词表编号”变成“模型后面可以继续计算的一组连续数值”？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里再引入两个词：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;token id&lt;/code&gt;：分词后查词表得到的离散编号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;embedding&lt;/code&gt;：把每个离散编号映射成长度为 &lt;code&gt;d_model&lt;/code&gt; 的可学习向量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先写最小版，不额外引入别的机制：&lt;/p&gt;</description></item><item><title>Hot100：从前序与中序遍历序列构造二叉树（Construct Binary Tree from Preorder and Inorder Traversal）索引分治 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/105-construct-binary-tree-from-preorder-and-inorder-traversal/</link><pubDate>Mon, 20 Apr 2026 09:37:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/105-construct-binary-tree-from-preorder-and-inorder-traversal/</guid><description>围绕 LeetCode 105 讲清前序负责定根、中序负责切左右的分工，以及如何用索引区间和哈希表把构树复杂度降到 O(n)。</description></item><item><title>Hot100：二叉树的右视图（Binary Tree Right Side View）层序遍历取每层最后一个 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/199-binary-tree-right-side-view/</link><pubDate>Mon, 20 Apr 2026 09:37:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/199-binary-tree-right-side-view/</guid><description>围绕 LeetCode 199 讲清为什么右视图等价于“每层最后一个节点”，以及按层 BFS 和右优先 DFS 两种稳定写法。</description></item><item><title>Hot100：二叉树展开为链表（Flatten Binary Tree to Linked List）反向先序重连 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/114-flatten-binary-tree-to-linked-list/</link><pubDate>Mon, 20 Apr 2026 09:37:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/114-flatten-binary-tree-to-linked-list/</guid><description>围绕 LeetCode 114 讲清为什么展开后的链表顺序就是先序遍历，以及如何用反向先序和 prev 指针把整棵树原地重连。</description></item><item><title>Hot100：二叉树中的最大路径和（Binary Tree Maximum Path Sum）树形 DP / 单边贡献 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/124-binary-tree-maximum-path-sum/</link><pubDate>Mon, 20 Apr 2026 09:37:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/124-binary-tree-maximum-path-sum/</guid><description>围绕 LeetCode 124 讲清最大路径和的关键设计：递归向上只返回单边贡献，而经过当前节点的完整路径用来更新全局答案。</description></item><item><title>Hot100：二叉搜索树中第 K 小的元素（Kth Smallest Element in a BST）中序计数 / 提前停止 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/230-kth-smallest-element-in-a-bst/</link><pubDate>Mon, 20 Apr 2026 09:37:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/230-kth-smallest-element-in-a-bst/</guid><description>围绕 LeetCode 230 讲清为什么 BST 的中序遍历天然有序，以及如何用显式栈做到边遍历边计数、命中第 k 个就提前停止。</description></item><item><title>Hot100：将有序数组转换为二叉搜索树（Convert Sorted Array to Binary Search Tree）分治选中点 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/108-convert-sorted-array-to-binary-search-tree/</link><pubDate>Mon, 20 Apr 2026 09:37:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/108-convert-sorted-array-to-binary-search-tree/</guid><description>围绕 LeetCode 108 讲清为什么每次选中点就能同时满足 BST 有序性和平衡性，以及索引分治写法为什么最稳定。</description></item><item><title>Hot100：二叉树的直径（Diameter of Binary Tree）树形 DP / 高度回传 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/543-diameter-of-binary-tree/</link><pubDate>Sun, 19 Apr 2026 14:52:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/543-diameter-of-binary-tree/</guid><description>围绕 LeetCode 543 讲清二叉树直径的核心：递归返回高度、在当前节点更新全局直径，以及为什么最长路径不一定只看根到叶。</description></item><item><title>Hot100：二叉树的最近公共祖先（Lowest Common Ancestor of a Binary Tree）后序返回值语义 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/236-lowest-common-ancestor-of-a-binary-tree/</link><pubDate>Sun, 19 Apr 2026 14:52:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/236-lowest-common-ancestor-of-a-binary-tree/</guid><description>围绕 LeetCode 236 讲清最近公共祖先的后序返回值语义：子树返回什么、什么时候当前节点成为答案、以及为什么节点自己也可以是祖先。</description></item><item><title>Hot100：验证二叉搜索树（Validate Binary Search Tree）区间约束 / 中序判序 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/98-validate-binary-search-tree/</link><pubDate>Sun, 19 Apr 2026 14:52:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/98-validate-binary-search-tree/</guid><description>围绕 LeetCode 98 讲清 BST 的祖先区间约束、不只和父节点比较的核心原因，以及区间递归与中序判序两种稳定写法。</description></item><item><title>Hot100：N 皇后严格增量构建教程</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/51-n-queens/</link><pubDate>Sun, 19 Apr 2026 00:09:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/51-n-queens/</guid><description>用严格增量构建的方式讲解 LeetCode 51 N 皇后：从最小例子、最小 DFS 骨架，到第一版正确解，再到列与对角线状态优化。</description></item><item><title>Hot100：分割回文串（Palindrome Partitioning）回溯 + 回文预处理 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/131-palindrome-partitioning/</link><pubDate>Sun, 19 Apr 2026 00:09:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/131-palindrome-partitioning/</guid><description>围绕 LeetCode 131 分割回文串，讲清“切分枚举 + 回文区间预处理”的完整推导过程，帮助你把字符串分割型回溯真正写稳。</description></item><item><title>LeetCode 216：组合总和 III（回溯 / 固定长度搜索）ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/216-combination-sum-iii/</link><pubDate>Fri, 17 Apr 2026 14:31:14 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/216-combination-sum-iii/</guid><description>围绕 LeetCode 216 讲清固定长度组合搜索、1..9 有界候选集与只能使用一次的回溯写法，并从题目本身一步一步推出最终模板。</description></item><item><title>LeetCode 40：组合总和 II（回溯 / 同层去重 / 只能用一次）ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/40-combination-sum-ii/</link><pubDate>Fri, 17 Apr 2026 14:31:11 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/40-combination-sum-ii/</guid><description>围绕 LeetCode 40 讲清“每个位置只能用一次”与“同层去重”的真正含义，并用一步一步推导的方式建立稳定回溯模板。</description></item><item><title>试过 MemPalace 之后，我为什么仍然选择 docs-first workflow</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/mempalace-vs-docs-first-workflow/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/mempalace-vs-docs-first-workflow/</guid><description>一次真实试用后，我把 MemPalace 和现有 docs / issue / commit 工作流逐项对比，结论是：它适合保存聊天中的长期上下文，但不适合充当正式 source of truth。</description></item><item><title>Hot100：电话号码的字母组合（Letter Combinations of a Phone Number）固定层数 DFS ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/17-letter-combinations-of-a-phone-number/</link><pubDate>Thu, 02 Apr 2026 13:48:57 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/17-letter-combinations-of-a-phone-number/</guid><description>围绕 LeetCode 17 讲清固定层数 DFS、数字到字母映射与多语言实现。</description></item><item><title>Hot100：全排列（Permutations）used[] 状态回溯模板 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/46-permutations/</link><pubDate>Thu, 02 Apr 2026 13:48:57 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/46-permutations/</guid><description>围绕 LeetCode 46 全排列，讲清 used[] 状态控制、叶子收集答案与多语言实现。</description></item><item><title>Hot100：组合总和（回溯 / 剪枝 / 可重复选取）ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/39-combination-sum/</link><pubDate>Thu, 02 Apr 2026 13:48:57 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/backtracking/39-combination-sum/</guid><description>围绕 LeetCode 39 讲清组合型回溯、可重复选取、remain 状态与排序剪枝，并用一步一步构建代码的方式把模板真正学会。</description></item><item><title>先写骨架，再补细节：用契约拆解算法题与中型程序</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/skeleton-first-contract-driven-coding/</link><pubDate>Fri, 20 Mar 2026 14:44:10 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/skeleton-first-contract-driven-coding/</guid><description>把复杂程序拆成骨架、契约和原子状态变换：本文用 LRUCache、下单流程与反例解释为什么先写外部结构通常更稳，以及 helper 应该如何控制副作用。</description></item><item><title>克隆图：哈希表 + DFS/BFS 实现无向图深拷贝（LeetCode 133）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/133-clone-graph/</link><pubDate>Thu, 19 Mar 2026 13:18:26 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/133-clone-graph/</guid><description>用原节点到新节点的映射表实现无向连通图深拷贝，覆盖 DFS 与 BFS 两种写法、环处理、正确性分析与多语言可运行实现。</description></item><item><title>Hot100：二叉树的层序遍历（Binary Tree Level Order Traversal）BFS / DFS ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/102-binary-tree-level-order-traversal/</link><pubDate>Sun, 15 Mar 2026 21:29:44 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/102-binary-tree-level-order-traversal/</guid><description>讲透 LeetCode 102 的按层 BFS、层宽控制与 DFS depth bucket 备选方案，附工程迁移和多语言实现。</description></item><item><title>Hot100：对称二叉树（Symmetric Tree）镜像递归 / BFS ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/101-symmetric-tree/</link><pubDate>Sun, 15 Mar 2026 21:29:43 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/101-symmetric-tree/</guid><description>围绕 LeetCode 101 讲清镜像递归、BFS 成对入队与树形结构对称校验，附工程迁移和多语言实现。</description></item><item><title>相同的树（Same Tree）同步递归 / BFS ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/100-same-tree/</link><pubDate>Sun, 15 Mar 2026 21:29:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/100-same-tree/</guid><description>围绕 LeetCode 100 讲清同步递归比较、队列成对校验与结构等价判断，附工程迁移与多语言实现。</description></item><item><title>Hot100：翻转二叉树（Invert Binary Tree）递归 / BFS ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/226-invert-binary-tree/</link><pubDate>Fri, 06 Mar 2026 17:58:23 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/226-invert-binary-tree/</guid><description>围绕 LeetCode 226 讲清树镜像变换、递归交换左右子树的本质，以及工程里的结构镜像场景。</description></item><item><title>Hot100：二叉树的最大深度（Maximum Depth of Binary Tree）DFS / BFS ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/104-maximum-depth-of-binary-tree/</link><pubDate>Fri, 06 Mar 2026 17:58:22 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/104-maximum-depth-of-binary-tree/</guid><description>讲透 LeetCode 104 的深度定义、递归 DFS 与层序 BFS 两种主流解法，附工程迁移和多语言实现。</description></item><item><title>Hot100：二叉树的中序遍历（Binary Tree Inorder Traversal）递归 / 显式栈 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/94-binary-tree-inorder-traversal/</link><pubDate>Fri, 06 Mar 2026 17:58:21 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/94-binary-tree-inorder-traversal/</guid><description>用左-根-右模板讲透 LeetCode 94，覆盖递归、显式栈与工程迁移场景，附多语言可运行实现。</description></item><item><title>先等价迁移，再行为改造：AI 协作时代最稳的工程工作流</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/equivalence-migration-then-behavior-refactor-workflow/</link><pubDate>Thu, 05 Mar 2026 16:54:59 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/equivalence-migration-then-behavior-refactor-workflow/</guid><description>一套面向 AI 协作开发的双阶段工作流：先做行为等价迁移，再做行为改造，配合任务记账与 worktree 隔离，减少返工与回归。</description></item><item><title>手写一个基础消息代理：发布、订阅、重试与失败契约</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/basic-message-broker/</link><pubDate>Sat, 28 Feb 2026 11:03:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/basic-message-broker/</guid><description>从发布订阅到重试与失败契约，系统讲解基础消息代理的实现与工程边界。</description></item><item><title>Git Worktree 使用教程：同仓库并行开发多个分支</title><link>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-worktree-usage-guide/</link><pubDate>Wed, 25 Feb 2026 09:27:18 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-worktree-usage-guide/</guid><description>一篇讲清 Git worktree 常用命令、典型场景、常见坑与最佳实践的中文教程，适合多分支并行开发与临时 hotfix。</description></item><item><title>Docker 常用使用教程：从入门到 Compose 实战（含 save/load 与权限排障）</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/docker-common-usage-tutorial/</link><pubDate>Thu, 12 Feb 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/docker-common-usage-tutorial/</guid><description>一篇覆盖 Docker 常用命令、Dockerfile、Compose、镜像 save/load 离线迁移与挂载权限排障的实战教程。</description></item><item><title>LeetCode 146：LRU 缓存设计（O(1)）哈希表 + 双向链表实战</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/146-lru-cache/</link><pubDate>Wed, 11 Feb 2026 08:02:05 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/146-lru-cache/</guid><description>LRUCache 的核心是用哈希表做 O(1) 定位、双向链表维护最近使用顺序，实现 get/put 平均 O(1)。本文按 ACERS 模板给出推导、工程应用与多语言代码。</description></item><item><title>LeetCode 138：随机链表的复制（Copy List with Random Pointer）深拷贝全解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/138-copy-list-with-random-pointer/</link><pubDate>Wed, 11 Feb 2026 07:59:32 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/138-copy-list-with-random-pointer/</guid><description>随机链表复制的核心是把“原节点身份”映射到“新节点身份”，再重建 next/random 指针。本文用 ACERS 结构给出思路推导、工程类比、常见坑和多语言可运行实现。</description></item><item><title>LeetCode 19：删除链表的倒数第 N 个结点（双指针一趟扫描）ACERS 全解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/19-remove-nth-node-from-end-of-list/</link><pubDate>Wed, 11 Feb 2026 07:56:02 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/19-remove-nth-node-from-end-of-list/</guid><description>从朴素两趟遍历到一趟快慢指针，系统讲清删除链表倒数第 N 个结点的思路、正确性、工程场景与多语言可运行实现。</description></item><item><title>LeetCode 2：两数相加（Add Two Numbers）链表进位从朴素到最优解</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/2-add-two-numbers/</link><pubDate>Wed, 11 Feb 2026 07:54:26 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/2-add-two-numbers/</guid><description>把两个逆序链表表示的非负整数相加，关键是逐位相加与进位传播。本文用 ACERS 结构讲透思路推导、工程类比与多语言可运行实现。</description></item><item><title>先定不变量与契约，再写实现：Evans/Fowler 实战法</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/invariants-contract-before-implementation/</link><pubDate>Wed, 11 Feb 2026 07:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/invariants-contract-before-implementation/</guid><description>解释“先定不变量/契约，再写实现”到底在工程上多了什么，并给出可执行的落地模板。</description></item><item><title>Hot100：排序链表（Sort List）链表归并排序 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/148-sort-list/</link><pubDate>Tue, 10 Feb 2026 17:07:38 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/148-sort-list/</guid><description>在 O(n log n) 时间内对单链表排序。本文用“快慢指针找中点 + 分治归并”给出稳定模板，并对比数组化方案、常见坑和多语言实现。</description></item><item><title>Hot100：合并K个升序链表（Merge k Sorted Lists）分治归并 O(N log k) ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/23-merge-k-sorted-lists/</link><pubDate>Tue, 10 Feb 2026 17:05:53 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/23-merge-k-sorted-lists/</guid><description>把 LeetCode 21 的双链表归并扩展到 k 路：从朴素串行合并到分治归并，时间复杂度降到 O(N log k)，并对比最小堆方案与工程取舍，附多语言实现。</description></item><item><title>Hot100：K 个一组翻转链表（Reverse Nodes in k-Group）分组反转 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/25-reverse-nodes-in-k-group/</link><pubDate>Tue, 10 Feb 2026 10:01:23 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/25-reverse-nodes-in-k-group/</guid><description>在单链表中每 k 个节点为一组进行原地反转，不足 k 的尾组保持不变。本文给出哑节点 + 分组扫描 + 原地反转模板，时间 O(n)、额外空间 O(1)，附多语言实现。</description></item><item><title>反转链表 II（Reverse Linked List II）哑节点+头插法 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/92-reverse-linked-list-ii/</link><pubDate>Tue, 10 Feb 2026 09:56:14 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/92-reverse-linked-list-ii/</guid><description>在单链表中仅反转区间 [left, right]：用哑节点定位前驱，再用头插法原地重排，时间 O(n)、额外空间 O(1)，附工程思路与多语言实现。</description></item><item><title>.dao 域名怎么选：ICANN、ENS、Handshake 一次讲清</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/web/dao-domain-suffix-ens-handshake-guide/</link><pubDate>Tue, 10 Feb 2026 09:33:01 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/web/dao-domain-suffix-ens-handshake-guide/</guid><description>从传统域名体系到 Web3 命名系统，系统说明 .dao 的真实现状、可选方案、落地步骤与风险边界。</description></item><item><title>图算法专题学习路径：从 BFS 到图计算模型</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/00-graph-algorithms-learning-path/</link><pubDate>Mon, 09 Feb 2026 10:14:45 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/00-graph-algorithms-learning-path/</guid><description>图算法专题导航与推荐阅读顺序，覆盖 BFS/DFS、可达性、最短路、CC/SCC、中心性、PageRank、社区发现、子图匹配、动态图、图分区与图计算模型。</description></item><item><title>图计算模型实战：Pregel（BSP）与 GAS，PageRank/CC/并行 BFS 怎么跑</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/110-graph-computation-models-pregel-gas-parallel-bfs/</link><pubDate>Mon, 09 Feb 2026 10:05:33 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/110-graph-computation-models-pregel-gas-parallel-bfs/</guid><description>围绕图计算模型给出工程化落地框架：Pregel 与 GAS 的核心抽象、同步语义、性能边界，以及 PageRank/CC/并行 BFS 的实现与选型。</description></item><item><title>图分区算法：Edge-cut vs Vertex-cut 与 METIS 工程解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/100-graph-partitioning-edge-cut-vertex-cut-metis/</link><pubDate>Mon, 09 Feb 2026 10:04:05 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/100-graph-partitioning-edge-cut-vertex-cut-metis/</guid><description>面向生产级图数据库的图分区实战文章，覆盖 Edge-cut 与 Vertex-cut 对比、METIS 核心流程、可运行示例和工程调优清单。</description></item><item><title>动态图与增量计算：增量最短路径、增量 PageRank、连通性维护 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/90-dynamic-graph-incremental-computation/</link><pubDate>Mon, 09 Feb 2026 10:00:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/90-dynamic-graph-incremental-computation/</guid><description>面向真实图系统，系统讲解动态图增量算法：增量最短路径、增量 PageRank、连通性维护。重点覆盖局部重算、延迟更新、近似结果三种工程必修技巧。</description></item><item><title>社区发现入门：Louvain 与 Label Propagation 的工程化选型 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/80-community-detection-louvain-label-propagation/</link><pubDate>Mon, 09 Feb 2026 09:59:58 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/80-community-detection-louvain-label-propagation/</guid><description>围绕社区发现的三类核心用途（群体识别、图分区、冷启动分析），系统讲解 Louvain 与 Label Propagation 的原理、复杂度、工程取舍与多语言可运行实现。</description></item><item><title>子图匹配 / 模式匹配：VF2 与 Ullmann 的工程化剪枝 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/70-subgraph-matching-vf2-ullmann-and-pruning/</link><pubDate>Mon, 09 Feb 2026 09:59:16 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/70-subgraph-matching-vf2-ullmann-and-pruning/</guid><description>系统讲解 Subgraph Isomorphism（NP-hard）与 VF2/Ullmann 核心思想，重点强调工程现实：受限模式查询与候选剪枝通常比算法名称本身更重要。</description></item><item><title>图中心性三件套：Degree、Betweenness、Closeness 工程 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/50-graph-centrality-degree-betweenness-closeness/</link><pubDate>Mon, 09 Feb 2026 09:56:11 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/50-graph-centrality-degree-betweenness-closeness/</guid><description>系统讲解图中心性三大指标：Degree、Betweenness、Closeness。重点给出工程可落地结论：大多数系统优先支持 Degree 和近似 Betweenness，并说明复杂度、近似策略与上线取舍。</description></item><item><title>PageRank / Personalized PageRank：图数据库节点重要性与增量更新 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/60-pagerank-and-personalized-pagerank/</link><pubDate>Mon, 09 Feb 2026 09:54:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/60-pagerank-and-personalized-pagerank/</guid><description>系统讲解 PageRank 与 Personalized PageRank：从迭代式计算、稀疏矩阵实现到增量更新策略，覆盖推荐与影响力分析等图数据库核心场景。</description></item><item><title>k-hop 与可达性查询：BFS 限制、Reachability 索引与 2-hop Labeling ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/30-k-hop-reachability-and-reach-index/</link><pubDate>Mon, 09 Feb 2026 09:52:17 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/30-k-hop-reachability-and-reach-index/</guid><description>围绕 k-hop 与可达性查询，系统讲解 BFS+hop 限制、为何一般不全算传递闭包、以及工程常用的位图索引/2-hop labeling 思路，并给出可运行多语言实现。</description></item><item><title>连通分量与强连通分量：Tarjan / Kosaraju 工程 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/40-connected-components-and-scc-tarjan-kosaraju/</link><pubDate>Mon, 09 Feb 2026 09:50:22 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/40-connected-components-and-scc-tarjan-kosaraju/</guid><description>系统讲解无向图 Connected Components 与有向图 SCC，重点覆盖 Tarjan（工程常用）与 Kosaraju，对应图数据库中的社区划分、子图切分与分片 hint。</description></item><item><title>最短路径三件套：BFS、Dijkstra、A* 工程实战 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/20-shortest-path-bfs-dijkstra-astar-acers/</link><pubDate>Mon, 09 Feb 2026 09:48:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/20-shortest-path-bfs-dijkstra-astar-acers/</guid><description>系统讲透最短路径三件套：无权图 BFS、非负权 Dijkstra、启发式 A*。覆盖多源 BFS、双向搜索、路径裁剪等工程优化，并附可运行代码与多语言模板。</description></item><item><title>BFS / DFS 工程入门：k-hop 查询、子图抽取与路径可达性 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/10-bfs-dfs-k-hop-subgraph-path-existence/</link><pubDate>Mon, 09 Feb 2026 09:44:11 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/graph/10-bfs-dfs-k-hop-subgraph-path-existence/</guid><description>围绕 k-hop 查询、子图抽取、路径可达性三类高频图任务，系统讲清 BFS/DFS 的迭代模板、early stop 剪枝与 visited bitmap/bloom 选型，并附多语言可运行实现。</description></item><item><title>AI 辅助编程不黑盒：责任主线工作流实战</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/ai-assisted-coding-responsibility-workflow/</link><pubDate>Sat, 07 Feb 2026 13:16:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/ai-assisted-coding-responsibility-workflow/</guid><description>用一套可执行的 commit 与分支流程，把 AI 变成加速器而不是黑盒来源。</description></item><item><title>Hot100：路径和 III 前缀和 + 哈希表统计向下路径（LeetCode 437）ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/437-path-sum-iii/</link><pubDate>Mon, 02 Feb 2026 22:13:45 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/binary-tree/437-path-sum-iii/</guid><description>用前缀和 + 计数哈希表在 O(n) 时间统计二叉树中和为 targetSum 的向下路径数，含推导、工程迁移与多语言实现。</description></item><item><title>Hot100：环形链表 II（Linked List Cycle II）Floyd 判环 + 定位入环点 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/142-linked-list-cycle-ii/</link><pubDate>Sun, 01 Feb 2026 21:40:21 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/142-linked-list-cycle-ii/</guid><description>不修改链表的前提下返回入环的第一个节点：Floyd 快慢指针先相遇判环，再从头与相遇点同步前进定位入环点；O(n) 时间、O(1) 额外空间。</description></item><item><title>Hot100：合并两个有序链表（Merge Two Sorted Lists）哨兵节点归并 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/21-merge-two-sorted-lists/</link><pubDate>Sun, 01 Feb 2026 21:40:06 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/21-merge-two-sorted-lists/</guid><description>用哨兵节点 + 双指针在 O(m+n) 时间把两个升序链表“拼接式”合并为一个升序链表，并对比递归写法与常见坑，附多语言可运行实现（Hot100）。</description></item><item><title>Hot100：环形链表（Linked List Cycle）Floyd 快慢指针 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/141-linked-list-cycle/</link><pubDate>Sun, 01 Feb 2026 18:45:44 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/141-linked-list-cycle/</guid><description>用 Floyd 快慢指针在 O(n) 时间、O(1) 额外空间判断单链表是否有环，并对比哈希表方案、常见坑与工程迁移，附多语言可运行实现（Hot100）。</description></item><item><title>Hot100：回文链表（Palindrome Linked List）快慢指针 + 反转后半段 O(1) 空间 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/234-palindrome-linked-list/</link><pubDate>Sun, 01 Feb 2026 18:44:01 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/234-palindrome-linked-list/</guid><description>判断单链表是否为回文：快慢指针定位中点、原地反转后半段并与前半段对比，最后再恢复链表；O(n) 时间、O(1) 额外空间。</description></item><item><title>Hot100：反转链表（Reverse Linked List）三指针迭代/递归 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/206-reverse-linked-list/</link><pubDate>Sun, 01 Feb 2026 16:30:26 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/206-reverse-linked-list/</guid><description>用三指针迭代在 O(n) 时间、O(1) 额外空间反转单链表，并对比递归写法与常见坑，附多语言可运行实现（Hot100）。</description></item><item><title>Hot100：相交链表（Intersection of Two Linked Lists）双指针换头 O(1) 空间 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/160-intersection-of-two-linked-lists/</link><pubDate>Sun, 01 Feb 2026 16:29:40 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/linked-list/160-intersection-of-two-linked-lists/</guid><description>在不破坏链表结构的前提下找到两个单链表的相交起点：双指针“走完 A 再走 B”保证同步，O(m+n) 时间、O(1) 额外空间；含推导、工程迁移与多语言实现。</description></item><item><title>Hot100：搜索二维矩阵 II（Search a 2D Matrix II）右上角阶梯搜索 O(m+n) ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/240-search-a-2d-matrix-ii/</link><pubDate>Sun, 01 Feb 2026 13:56:55 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/240-search-a-2d-matrix-ii/</guid><description>行列均升序的矩阵查找 target：从右上角出发每步排除一行或一列，O(m+n) 时间、O(1) 空间；含推导、工程迁移与多语言实现。</description></item><item><title>Hot100：螺旋矩阵（Spiral Matrix）边界收缩模拟 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/54-spiral-matrix/</link><pubDate>Sun, 01 Feb 2026 13:55:22 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/54-spiral-matrix/</guid><description>用“边界收缩”在 O(mn) 时间输出矩阵的顺时针螺旋序列，附工程场景、易错点与多语言实现（Hot100）。</description></item><item><title>Hot100：旋转图像（Rotate Image）转置 + 行反转实现原地 90 度旋转 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/48-rotate-image/</link><pubDate>Sun, 01 Feb 2026 13:51:51 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/48-rotate-image/</guid><description>顺时针旋转 n×n 矩阵 90 度：用“先转置、再反转每一行”在 O(n^2) 时间、O(1) 额外空间原地完成；含推导、工程场景与多语言实现。</description></item><item><title>矩阵置零：用首行首列做标记实现原地 O(1) 空间（LeetCode 73）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/73-set-matrix-zeroes/</link><pubDate>Sun, 01 Feb 2026 12:53:35 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/73-set-matrix-zeroes/</guid><description>把首行首列当作标记位，在不额外开集合的前提下原地完成矩阵置零；含思路推导、常见坑与多语言实现。</description></item><item><title>Windows 双网络分流：公司 WiFi 走内网，手机 USB 共享走外网</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/windows-wifi-intranet-usb-tether-split-routing/</link><pubDate>Fri, 30 Jan 2026 08:57:22 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/windows-wifi-intranet-usb-tether-split-routing/</guid><description>在 Windows 上同时连接公司 WiFi 和手机 USB 网络共享，通过跃点数（metric）与静态路由实现“内网走公司、外网走手机”。</description></item><item><title>WireGuard Split Tunnel 实战：手机热点上外网，同时访问公司内网</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/wireguard-split-tunnel-company-intranet/</link><pubDate>Thu, 29 Jan 2026 16:15:21 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/wireguard-split-tunnel-company-intranet/</guid><description>从 0 搭建 WireGuard 内网 VPN，并用 Split Tunnel 实现“外网走手机热点、内网走 VPN”；覆盖公网/无公网两种部署与排错清单。</description></item><item><title>Tailscale 子网路由实战：外网访问公司内网的最稳方案</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/tailscale-subnet-router-guide/</link><pubDate>Thu, 29 Jan 2026 16:13:14 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/tailscale-subnet-router-guide/</guid><description>一篇可直接落地的 Tailscale 子网路由教程，解决无公网 IP 情况下的内网访问与分流。</description></item><item><title>一台电脑同时连公司内网和手机外网：双网络分流实战</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/dual-network-split-routing/</link><pubDate>Thu, 29 Jan 2026 16:00:40 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/dual-network-split-routing/</guid><description>讲清楚一台电脑如何同时访问公司内网与手机外网，覆盖 USB 共享、双 WiFi、静态路由与 VPN 分流。</description></item><item><title>请求日志一定要带 RequestId 吗？Python 成熟实践与落地指南</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/request-id-logging-best-practices/</link><pubDate>Thu, 29 Jan 2026 15:16:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/request-id-logging-best-practices/</guid><description>系统回答“日志是否需要 requestId”，并给出 Python 工业级自动注入方案、工程场景与排查流程。</description></item><item><title>Attention Is All You Need：Transformer 的核心算法与工程落地</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/attention-is-all-you-need/</link><pubDate>Sun, 25 Jan 2026 20:08:41 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/attention-is-all-you-need/</guid><description>从算法抽象、复杂度与工程约束出发，解释 Transformer 如何用注意力替代递归与卷积，并给出可运行示例与选型指南。</description></item><item><title>FlashAttention 的 MQA/GQA：共享 KV 的等价、收益与实现要点（含可运行验证）</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/flash-attention-mqa-gqa/</link><pubDate>Sun, 25 Jan 2026 12:51:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/flash-attention-mqa-gqa/</guid><description>解释 FlashAttention 如何处理 MQA/GQA：共享 KV、按组计算与内存复用策略，并附可运行示例验证等价性。</description></item><item><title>FlashAttention 为什么能 one-pass：在线 softmax（m/l）与 Tiling 的核心思想</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/flash-attention-one-pass-and-tiling/</link><pubDate>Sun, 25 Jan 2026 12:51:14 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/flash-attention-one-pass-and-tiling/</guid><description>解释 FlashAttention 的 one-pass 计算原理与 tiling 策略：在线 softmax（m,l）更新、流式累积输出、不落地 $QK^\top$ 与概率矩阵，并给出可运行的块级注意力验证与访存算账。</description></item><item><title>Softmax 工程实现与 GPU 访存优化：在线更新、融合与带宽算账（含可运行验证）</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/softmax-gpu-memory-io-optimization/</link><pubDate>Sun, 25 Jan 2026 12:51:13 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/softmax-gpu-memory-io-optimization/</guid><description>拆解 softmax 标准计算的访存问题，并给出在线 softmax 与融合实现的工程优化思路，包含可运行示例与带宽算账。</description></item><item><title>Self-Attention 计算公式与 Softmax 数值稳定：从推导到工程实现</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/self-attention-softmax-formula-and-stability/</link><pubDate>Sun, 25 Jan 2026 12:50:33 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/self-attention-softmax-formula-and-stability/</guid><description>用公式与可运行示例讲清 Self-Attention 的计算流程、softmax 的数值问题与工程实现要点。</description></item><item><title>单阶段 vs 双阶段目标检测：从候选集合到 NMS 的工程算账</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/single-stage-vs-two-stage-object-detection/</link><pubDate>Sat, 24 Jan 2026 16:36:30 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/single-stage-vs-two-stage-object-detection/</guid><description>系统对比单阶段与双阶段目标检测的流程、复杂度与工程场景：候选集合规模、NMS/后处理成本、focal loss 与采样策略，并给出纯 NumPy 可运行示例用于算账与验证。</description></item><item><title>Anchor-Based vs Anchor-Free：目标检测两条路线</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/anchor-based-vs-anchor-free/</link><pubDate>Sat, 24 Jan 2026 16:36:19 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/anchor-based-vs-anchor-free/</guid><description>对比 Anchor-based 与 Anchor-free 检测框架的核心差异、工程取舍与实战场景。</description></item><item><title>IoU 是什么：目标检测评估的核心指标</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/iou-explained/</link><pubDate>Sat, 24 Jan 2026 16:34:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/iou-explained/</guid><description>从公式到工程实践解释 IoU（交并比），并给出可运行示例与评估细节。</description></item><item><title>空洞卷积（Dilated Convolution）：扩大感受野的工程利器</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/dilated-convolution/</link><pubDate>Sat, 24 Jan 2026 16:33:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/dilated-convolution/</guid><description>系统讲清空洞卷积的原理、复杂度与工程应用，并给出最小 PyTorch 示例。</description></item><item><title>NMS 描述：非极大值抑制的原理与工程实践</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/nms-overview/</link><pubDate>Sat, 24 Jan 2026 16:32:59 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/nms-overview/</guid><description>系统讲清 NMS 的核心流程、IoU 计算与工程取舍，并给出最小 PyTorch 示例。</description></item><item><title>CNN 参数量计算：从卷积核到整网规模</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/cnn-parameter-count/</link><pubDate>Sat, 24 Jan 2026 16:28:40 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/cnn-parameter-count/</guid><description>系统讲清 CNN 参数量计算方法与常见陷阱，并给出最小 PyTorch 示例。</description></item><item><title>CNN、RNN、LSTM 与 Transformer 的区别与适用场景</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/architecture/cnn-rnn-lstm-transformer-comparison/</link><pubDate>Sat, 24 Jan 2026 16:28:18 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/architecture/cnn-rnn-lstm-transformer-comparison/</guid><description>从依赖路径长度与资源复杂度两个核心概念出发，系统对比 CNN、RNN、LSTM 与 Transformer，并给出可运行示例与工程选型步骤。</description></item><item><title>动量（Momentum）优化的过程：从直觉到公式</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/momentum-optimizer-process/</link><pubDate>Sat, 24 Jan 2026 16:28:18 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/momentum-optimizer-process/</guid><description>解释动量优化的更新过程、直觉与工程取舍，并给出最小 PyTorch 示例。</description></item><item><title>优化器的了解：从 SGD 到 Adam 的工程取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/optimizer-overview/</link><pubDate>Sat, 24 Jan 2026 16:27:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/optimizer-overview/</guid><description>系统讲清常见优化器原理与工程取舍，含最小 PyTorch 示例与实践建议。</description></item><item><title>图像自编码是怎么做的：原理、流程与最小实现</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/image-autoencoder-how/</link><pubDate>Sat, 24 Jan 2026 16:26:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/image-autoencoder-how/</guid><description>系统讲清图像自编码的结构、训练目标与工程场景，并给出最小 PyTorch 示例。</description></item><item><title>ViT 结构描述：从 Patch Embedding 到 Transformer 编码器</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/vit-architecture-overview/</link><pubDate>Sat, 24 Jan 2026 16:25:35 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/vision/vit-architecture-overview/</guid><description>系统讲清 ViT 的结构组件、工作流程与工程实践，并给出最小 PyTorch 示例。</description></item><item><title>BN 与 Dropout：训练与推理时的关键区别</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/bn-vs-dropout-train-infer/</link><pubDate>Sat, 24 Jan 2026 16:24:44 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/bn-vs-dropout-train-infer/</guid><description>系统对比 BatchNorm 与 Dropout 在训练/推理阶段的行为差异，并提供最小 PyTorch 示例。</description></item><item><title>Transformer 中可以用 BatchNorm 吗？</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/batchnorm-in-transformer/</link><pubDate>Sat, 24 Jan 2026 16:24:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/batchnorm-in-transformer/</guid><description>讨论 Transformer 使用 BatchNorm 的可行性、限制与工程取舍，并给出最小示例。</description></item><item><title>BN 与 LN 的区别：训练稳定性与工程取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/batchnorm-vs-layernorm/</link><pubDate>Sat, 24 Jan 2026 16:23:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/batchnorm-vs-layernorm/</guid><description>对比 BatchNorm 与 LayerNorm 的原理、适用场景与工程代价，并提供最小 PyTorch 示例。</description></item><item><title>为什么注意力要除以 √(d_k)：从数值稳定到工程收益</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/why-scale-attention-by-sqrt-dk/</link><pubDate>Sat, 24 Jan 2026 16:22:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/why-scale-attention-by-sqrt-dk/</guid><description>解释注意力中 QK^T 为何需要除以 √(d_k)，并给出最小 PyTorch 示例与工程场景。</description></item><item><title>残差连接的作用：为什么深度网络离不开它</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/residual-connection-role/</link><pubDate>Sat, 24 Jan 2026 16:22:22 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/residual-connection-role/</guid><description>解释残差连接在深度网络中的作用与原理，并提供最小可运行示例。</description></item><item><title>Attention 的复杂度与为什么需要位置编码</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/attention-complexity-and-positional-encoding/</link><pubDate>Sat, 24 Jan 2026 16:21:51 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/attention-complexity-and-positional-encoding/</guid><description>解释注意力的时间/空间复杂度，并说明位置编码对序列建模的必要性，含最小示例。</description></item><item><title>为什么使用多头注意力机制：能力、稳定性与工程取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/why-multi-head-attention/</link><pubDate>Sat, 24 Jan 2026 16:20:59 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/why-multi-head-attention/</guid><description>用 ACERS 框架解释多头注意力的必要性、核心原理与工程场景，并给出最小可运行示例。</description></item><item><title>为什么 GPT 是 Decoder-Only：自回归生成的最佳形态</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/why-gpt-decoder-only/</link><pubDate>Sat, 24 Jan 2026 16:15:34 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/why-gpt-decoder-only/</guid><description>解释 GPT 选择 decoder-only 结构的原因，并与 encoder-only / encoder-decoder 做工程对比。</description></item><item><title>BERT vs GPT：预训练任务与应用差异</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/bert-vs-gpt-pretraining-objectives/</link><pubDate>Sat, 24 Jan 2026 16:12:12 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/bert-vs-gpt-pretraining-objectives/</guid><description>对比 BERT 与 GPT 的预训练目标、架构假设与工程场景，并给出最小可运行示例。</description></item><item><title>SGD vs Adam：优化器原理与工程取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/sgd-vs-adam-optimizer/</link><pubDate>Sat, 24 Jan 2026 16:12:12 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/sgd-vs-adam-optimizer/</guid><description>对比 SGD 与 Adam 的原理、收敛特性与应用场景，并提供最小 PyTorch 示例。</description></item><item><title>LoRA 初始化的常见方法与工程取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/lora-initialization-methods/</link><pubDate>Sat, 24 Jan 2026 16:00:02 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/lora-initialization-methods/</guid><description>系统对比 LoRA 的常见初始化方式，并给出最小 PyTorch 示例与工程实践建议。</description></item><item><title>LLaMA 中 RMSNorm 相比 LayerNorm 的优势</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/rmsnorm-vs-layernorm-llama/</link><pubDate>Sat, 24 Jan 2026 15:52:58 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/llm/rmsnorm-vs-layernorm-llama/</guid><description>从公式、复杂度与工程实践出发，解析 LLaMA 选择 RMSNorm 的原因，并给出最小 PyTorch 示例。</description></item><item><title>Go 并发机制一文通：goroutine、channel、同步/异步与典型场景</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/go/go-concurrency-mechanisms/</link><pubDate>Sat, 24 Jan 2026 15:47:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/go/go-concurrency-mechanisms/</guid><description>系统讲清 Go 并发的核心机制与同步/异步差异，并通过真实场景给出可运行示例与实践建议。</description></item><item><title>Self-Attention vs Cross-Attention：机制、差异与工程应用</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/self-attention-vs-cross-attention/</link><pubDate>Sat, 24 Jan 2026 15:44:12 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/attention/self-attention-vs-cross-attention/</guid><description>用 ACERS 框架讲清 self-attention 与 cross-attention 的核心差异、公式与工程场景。</description></item><item><title>闭包和类的共同点：为什么它们都能“携带状态”</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/closures-vs-classes/</link><pubDate>Sat, 24 Jan 2026 15:42:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/closures-vs-classes/</guid><description>对比闭包与类的共同点与差异，解释它们如何封装状态。</description></item><item><title>什么时候适用函数式语言：场景与边界</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/when-to-use-functional-languages/</link><pubDate>Sat, 24 Jan 2026 15:42:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/when-to-use-functional-languages/</guid><description>讨论函数式语言的适用场景、优势与限制。</description></item><item><title>为什么函数式编程越来越受关注</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/why-fp-popular-now/</link><pubDate>Sat, 24 Jan 2026 15:42:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/why-fp-popular-now/</guid><description>从并发、可靠性与工程规模角度解释函数式编程的流行原因。</description></item><item><title>用多态替换 if：把流程判断变成对象职责</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/replace-if-with-polymorphism/</link><pubDate>Sat, 24 Jan 2026 15:42:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/replace-if-with-polymorphism/</guid><description>通过对象职责拆分，消除重复 if 判断。</description></item><item><title>用多态替换 switch：让代码更符合开闭原则</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/replace-switch-with-polymorphism/</link><pubDate>Sat, 24 Jan 2026 15:42:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/replace-switch-with-polymorphism/</guid><description>通过策略模式消除 switch 语句，降低分支扩展成本。</description></item><item><title>BLIP/BLIP-2 实战原理与最小推理示例</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/blip/blip-blip2-principles-minimal-inference/</link><pubDate>Sat, 24 Jan 2026 15:40:51 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/blip/blip-blip2-principles-minimal-inference/</guid><description>按 ACERS 结构讲清 BLIP 与 BLIP-2 的原理差异，并给出最小 PyTorch 推理示例。</description></item><item><title>BLIP 与 BLIP-2 架构和区别：从对齐到生成</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/blip/blip-vs-blip2-architecture/</link><pubDate>Sat, 24 Jan 2026 15:35:34 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/blip/blip-vs-blip2-architecture/</guid><description>用结构化视角解释 BLIP 与 BLIP-2 的训练目标、模块设计与工程差异。</description></item><item><title>“现在请你面试我”：如何把开放式问题变成结构化对话</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/interview-me-guide/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/interview-me-guide/</guid><description>提供把开放式问题转化为结构化面试的提问框架。</description></item><item><title>10 年后的你：如何用技术规划长期成长</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/you-in-10-years/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/you-in-10-years/</guid><description>用工程视角回答“10 年后的你”，并给出成长路径框架。</description></item><item><title>把镜子放在扫描仪上会发生什么？背后的物理与设计</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/mirror-on-scanner/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/mirror-on-scanner/</guid><description>从光学原理解释“镜子放在扫描仪上”的结果，并延伸到产品设计思维。</description></item><item><title>老板让你撒谎怎么办：原则、边界与应对</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/boss-asks-to-lie-response/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/boss-asks-to-lie-response/</guid><description>从职业伦理与风险控制角度回答“被要求撒谎”的问题。</description></item><item><title>如果能穿越回去：给年轻自己的技术建议</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/time-travel-advice/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/time-travel-advice/</guid><description>用工程视角给出可执行的长期成长建议。</description></item><item><title>如果上司是你的克隆人，你愿意共事吗？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/clone-boss-would-you-work/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/clone-boss-would-you-work/</guid><description>从协作、反馈与角色关系角度分析“克隆上司”的开放式问题。</description></item><item><title>如果我是你老板被解雇，你会如何通知我？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/how-to-notify-firing/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/how-to-notify-firing/</guid><description>从尊重、透明与风险控制角度回答“解雇通知”的开放式问题。</description></item><item><title>为 COBOL 辩护：为什么老语言仍有价值</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/defend-cobol/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/defend-cobol/</guid><description>从稳定性、领域适配与成本角度讨论 COBOL 的价值。</description></item><item><title>为什么 Quora 的回答质量更好：机制与激励</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/quora-vs-yahoo-answers/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/quora-vs-yahoo-answers/</guid><description>从产品机制与激励设计解释 Quora 与 Yahoo Answers 的差异。</description></item><item><title>重构还是重写：如何评估系统演进路径</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/general/refactor-vs-rewrite-debate/</link><pubDate>Sat, 24 Jan 2026 15:35:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/general/refactor-vs-rewrite-debate/</guid><description>给出重构 vs 重写的决策框架，避免拍脑袋。</description></item><item><title>Active Record 的限制与缺陷：为什么它不适合复杂领域</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/active-record-limitations/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/active-record-limitations/</guid><description>分析 Active Record 模式的局限性，并给出适用边界与替代方案。</description></item><item><title>Java 栈的内存泄漏：为什么 pop 之后仍然占用</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/java-stack-memory-leak/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/java-stack-memory-leak/</guid><description>通过经典栈实现示例解释 Java 的内存泄漏与修复方式。</description></item><item><title>JavaScript for 循环闭包陷阱：为什么会打印 3</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/web/js-closure-for-loop/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/web/js-closure-for-loop/</guid><description>解释 for 循环中的闭包问题，并给出正确修复方式。</description></item><item><title>从循环到递归：如何避免可变状态</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/loop-to-recursion-immutability/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/loop-to-recursion-immutability/</guid><description>演示把循环转换成递归的思路，并说明如何减少可变状态。</description></item><item><title>类型擦除示例：为什么 ArrayList&lt;Integer&gt; 与 ArrayList&lt;Float&gt; 相等</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/java-type-erasure-example/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/java-type-erasure-example/</guid><description>解释 Java 类型擦除的机制与常见误区。</description></item><item><title>如何重构嵌套错误码：从深层 if 到清晰流程</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/refactor-nested-error-codes/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/refactor-nested-error-codes/</guid><description>用早返回与小函数拆分重构深层嵌套错误处理。</description></item><item><title>设计 vs 架构：范围、抽象层级与责任</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/design-vs-architecture/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/design-vs-architecture/</guid><description>从范围与抽象层级解释设计与架构的区别。</description></item><item><title>数据抽象被破坏的例子：为什么实现细节不该外泄</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/data-abstraction-violation/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/data-abstraction-violation/</guid><description>通过示例说明数据抽象被破坏的后果，并给出修复方法。</description></item><item><title>为什么 Git/Mercurial 的合并比 SVN/CVS 更容易</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/merge-easier-git-vs-svn/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/merge-easier-git-vs-svn/</guid><description>从历史模型与合并算法解释分布式版本控制系统的优势。</description></item><item><title>为什么事件驱动架构能提升可扩展性</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/event-driven-scalability/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/event-driven-scalability/</guid><description>解释事件驱动架构的扩展优势、适用场景与工程取舍。</description></item><item><title>我最喜欢的语言的三个缺陷：以 Python 为例</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/three-worst-flaws-python/</link><pubDate>Sat, 24 Jan 2026 15:29:20 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/three-worst-flaws-python/</guid><description>从工程角度分析 Python 的三大缺陷及其应对策略。</description></item><item><title>本周我学到了什么：工程师的复盘模板</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/what-did-you-learn-this-week/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/what-did-you-learn-this-week/</guid><description>给出每周学习复盘的结构化模板，帮助持续成长。</description></item><item><title>浏览器公司如何盈利：产品入口与商业化路径</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/web/browser-monetization/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/web/browser-monetization/</guid><description>解释浏览器厂商的主要盈利模式与产品策略。</description></item><item><title>敏捷宣言的两句核心话：个体与协作的优先级</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/agile-manifesto-values/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/agile-manifesto-values/</guid><description>讨论“个体与交互重于过程和工具”“客户协作重于合同谈判”的含义与落地方式。</description></item><item><title>设计、美学与功能性的关系：如何做取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/beauty-vs-functionality/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/beauty-vs-functionality/</guid><description>讨论设计、美学与功能之间的关系，并给出可操作的取舍原则。</description></item><item><title>设计中的美学元素：朋友还是敌人？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/aesthetics-friend-or-foe/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/aesthetics-friend-or-foe/</guid><description>讨论美学在产品设计中的作用、风险与平衡。</description></item><item><title>项目经理有用吗：角色价值与协作边界</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/are-project-managers-useful/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/are-project-managers-useful/</guid><description>讨论项目经理的价值、风险与协作方式。</description></item><item><title>只有一周能改善同事生活？可落地的工程清单</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/week-to-improve-colleagues/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/week-to-improve-colleagues/</guid><description>给出一周内可交付的工程改进清单，提升团队日常体验。</description></item><item><title>最近读过的 5 本书：工程师视角的清单</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/five-books-recently/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/five-books-recently/</guid><description>给出工程师常读的 5 本书清单，并说明适用场景。</description></item><item><title>作为 CTO 你会如何决策：从战略到执行的框架</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/cto-decision-framework/</link><pubDate>Sat, 24 Jan 2026 13:35:15 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/cto-decision-framework/</guid><description>提供 CTO 视角下的决策框架：业务、架构、组织与风险。</description></item><item><title>“喜欢这个的人也喜欢…”：电商推荐的最小实现</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/recommendation-people-also-like/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/recommendation-people-also-like/</guid><description>用协同过滤的简化模型实现“相似商品推荐”。</description></item><item><title>80 年代后的 CPU 变化与编程影响</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/cpu-changes-after-80s/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/cpu-changes-after-80s/</guid><description>概述 CPU 从频率提升到多核与缓存层级的演进，并解释对编程的影响。</description></item><item><title>Saga 与补偿操作：分布式流程的核心区别</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/saga-vs-compensation/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/saga-vs-compensation/</guid><description>解释 Saga 与补偿操作的关系，以及在 SOA/微服务中的落地方式。</description></item><item><title>SOA 与微服务的区别：边界、治理与演进方式</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/soa-vs-microservices/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/soa-vs-microservices/</guid><description>对比 SOA 与微服务在服务粒度、治理方式与演进路径上的差异。</description></item><item><title>Web 服务版本管理：兼容性与重大变更的策略</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/web-service-versioning/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/web-service-versioning/</guid><description>讨论 API 版本管理、兼容性策略与重大变更的工程实践。</description></item><item><title>除了攻击之外，哪些设计会导致拒绝服务</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/design-issues-causing-dos/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/design-issues-causing-dos/</guid><description>讨论架构设计缺陷如何引发类似 DoS 的系统故障。</description></item><item><title>创新与可预测性交存：如何兼顾探索与交付</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/innovation-vs-predictability/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/innovation-vs-predictability/</guid><description>给出同时追求创新与可预测交付的组织策略。</description></item><item><title>分布式系统中的故障切换与会话管理</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/failover-and-user-session/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/failover-and-user-session/</guid><description>解释故障切换与用户会话管理的核心策略与工程取舍。</description></item><item><title>如何设计去中心化 P2P 系统：节点、发现与一致性</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/design-decentralized-p2p-system/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/design-decentralized-p2p-system/</guid><description>从节点发现、路由与一致性角度介绍 P2P 系统设计。</description></item><item><title>如何向祖母解释线程：一个厨房的类比</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/explain-thread-to-grandma/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/explain-thread-to-grandma/</guid><description>用生活类比解释线程与并发的基本概念。</description></item><item><title>软件开发是艺术、技艺还是工程？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/software-dev-art-craft-engineering/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/software-dev-art-craft-engineering/</guid><description>从工程管理视角讨论软件开发的本质与平衡。</description></item><item><title>什么样的代码可读性强：结构、命名与认知负担</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/readable-code-principles/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/readable-code-principles/</guid><description>从结构、命名与认知负担角度定义“可读性强的代码”。</description></item><item><title>统一设计是否意味着架构师的贵族统治？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/unity-of-design-aristocracy/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/unity-of-design-aristocracy/</guid><description>讨论“统一设计”与团队自治之间的张力与平衡。</description></item><item><title>微服务架构的优劣：收益、成本与适用场景</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/microservices-pros-cons/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/microservices-pros-cons/</guid><description>总结微服务的优势与代价，并给出选型建议。</description></item><item><title>微服务太“微”会发生什么：边界拆分的警戒线</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/when-microservices-too-micro/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/when-microservices-too-micro/</guid><description>讨论微服务拆分过细的代价与判断标准。</description></item><item><title>为什么 CGI 的扩展性不好：进程模型的代价</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/cgi-scalability-issues/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/cgi-scalability-issues/</guid><description>解释 CGI 进程模型的性能瓶颈与扩展性问题。</description></item><item><title>为什么长期事务不被看好：Saga 的现实优势</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/long-lived-transactions-vs-saga/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/soa/long-lived-transactions-vs-saga/</guid><description>解释长期事务的问题以及 Saga 模式的工程优势与适用场景。</description></item><item><title>性能生命周期：从设计到上线的全流程管理</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/performance-lifecycle-management/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/performance-lifecycle-management/</guid><description>说明性能从需求、设计、开发到上线的生命周期管理要点。</description></item><item><title>在瀑布式公司引入持续交付：渐进式落地路线</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/introduce-continuous-delivery/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/introduce-continuous-delivery/</guid><description>给出在瀑布流程中推动持续交付的实际步骤与策略。</description></item><item><title>重复造轮子、NIH 与狗粮文化：何时有价值</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/reinventing-wheel-nih-dogfooding/</link><pubDate>Sat, 24 Jan 2026 13:27:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/reinventing-wheel-nih-dogfooding/</guid><description>讨论重复造轮子、非我发明症与狗粮文化的取舍。</description></item><item><title>对比学习损失函数系列（1/4）：对比损失 Contrastive Loss</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/contrastive-learning/1-contrastive-loss-function/</link><pubDate>Sat, 24 Jan 2026 13:22:02 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/contrastive-learning/1-contrastive-loss-function/</guid><description>从公式到实验，系统理解对比损失（Contrastive Loss）如何拉近正样本、推远负样本。</description></item><item><title>对比学习损失函数系列（2/4）：三元组损失 Triplet Loss</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/contrastive-learning/2-triplet-loss-function/</link><pubDate>Sat, 24 Jan 2026 13:22:02 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/contrastive-learning/2-triplet-loss-function/</guid><description>从 anchor-positive-negative 视角理解 Triplet Loss，并用最小可运行实验掌握 hard negative mining。</description></item><item><title>对比学习损失函数系列（3/4）：InfoNCE 与 SimCLR</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/contrastive-learning/3-infonce-simclr/</link><pubDate>Sat, 24 Jan 2026 13:22:02 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/contrastive-learning/3-infonce-simclr/</guid><description>从 InfoNCE 公式到 SimCLR 训练流程，理解自监督对比学习的关键设计。</description></item><item><title>对比学习损失函数系列（4/4）：CLIP 对比学习目标</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/contrastive-learning/4-clip-contrastive-learning-objective/</link><pubDate>Sat, 24 Jan 2026 13:22:02 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/contrastive-learning/4-clip-contrastive-learning-objective/</guid><description>从损失函数视角理解 CLIP 的双向对比学习目标，建立跨模态对齐的核心直觉。</description></item><item><title>CPU 空闲时在做什么：调度、节能与后台任务</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/system/what-happens-when-cpu-idle/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/system/what-happens-when-cpu-idle/</guid><description>解释 CPU 空闲时的调度行为、节能状态与系统后台活动。</description></item><item><title>从 REPL 到逆波兰计算器：一步步扩展交互程序</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/repl-to-rpn-calculator/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/repl-to-rpn-calculator/</guid><description>从最小可用 REPL 开始，逐步演化为逆波兰表达式计算器。</description></item><item><title>内存泄漏示例：为什么不释放会出问题</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/memory-leak-example/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/memory-leak-example/</guid><description>通过示例说明内存泄漏的成因与规避策略。</description></item><item><title>如何排序 10GB 文件：外部排序的工程方案</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/sort-10gb-file/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/sort-10gb-file/</guid><description>解释外部排序的流程、分块策略与磁盘 I/O 优化。</description></item><item><title>如何排序 10TB 数据：分布式排序思路</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/sort-10tb-data/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/sort-10tb-data/</guid><description>介绍大规模分布式排序的基本流程与工程考量。</description></item><item><title>如何设计磁盘碎片整理：目标、步骤与权衡</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/filesystem-defragmentation-design/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/filesystem-defragmentation-design/</guid><description>从工程角度设计碎片整理流程，并给出简化可运行示例。</description></item><item><title>如何向 5 岁孩子解释 Unicode</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/explain-unicode-to-kid/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/explain-unicode-to-kid/</guid><description>用简单类比解释 Unicode 的概念与作用。</description></item><item><title>如何向 5 岁孩子解释数据库事务</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/explain-transaction-to-kid/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/explain-transaction-to-kid/</guid><description>用生活类比解释事务的原子性与一致性。</description></item><item><title>实现 rnd()：从基础随机数到可控随机</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/implement-rnd-function/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/implement-rnd-function/</guid><description>解释随机数生成的基本思路，并用线性同余法实现一个简单 rnd()。</description></item><item><title>手写一个最小的垃圾回收器：标记-清除模型</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/simple-garbage-collector/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/simple-garbage-collector/</guid><description>用最小示例解释标记-清除 GC 的核心思路。</description></item><item><title>随机迷宫生成：深度优先回溯法</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/random-maze-generator/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/random-maze-generator/</guid><description>用深度优先回溯生成迷宫，并提供可运行示例。</description></item><item><title>随机生成不重复序列：洗牌法与采样法</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/random-unique-sequence/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/random-unique-sequence/</guid><description>解释如何生成不重复随机序列，并给出洗牌法实现。</description></item><item><title>尾递归阶乘：把递归变成可优化形式</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/tail-recursive-factorial/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/tail-recursive-factorial/</guid><description>通过尾递归形式实现阶乘，并讨论实际语言支持情况。</description></item><item><title>写一个基础 Web 服务器：最小可用实现</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/basic-web-server/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/basic-web-server/</guid><description>用最小可运行示例解释 HTTP 服务的核心流程。</description></item><item><title>用队列实现栈：单队列旋转法</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/queue-to-stack/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/queue-to-stack/</guid><description>解释如何用队列模拟栈，并给出可运行实现与复杂度分析。</description></item><item><title>用栈实现队列：双栈法的思路与实现</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/stack-to-queue/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/stack-to-queue/</guid><description>解释如何用两个栈模拟队列，并给出可运行实现与复杂度分析。</description></item><item><title>栈溢出示例：递归深度与调用栈的边界</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/stack-overflow-example/</link><pubDate>Sat, 24 Jan 2026 13:20:24 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/algorithm/stack-overflow-example/</guid><description>展示栈溢出的典型示例，并解释成因与规避方法。</description></item><item><title>Java 与 C# 的互操作性：可行路径与现实约束</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/java-csharp-interoperability/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/java-csharp-interoperability/</guid><description>讨论 Java 与 C# 的互操作方式、常见方案与工程取舍。</description></item><item><title>多继承 vs 多接口：对“正交性”的影响</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/multiple-inheritance-vs-interfaces-orthogonality/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/multiple-inheritance-vs-interfaces-orthogonality/</guid><description>对比多继承与多接口对正交性与复杂度的影响，并给出实践建议。</description></item><item><title>反腐败层（ACL）：如何隔离外部系统的复杂性</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/anti-corruption-layer/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/anti-corruption-layer/</guid><description>解释反腐败层的作用、使用场景与实现方式。</description></item><item><title>泛型协变与逆变：为什么 List&lt;Cat&gt; 不是 List&lt;Animal&gt;</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/generics-variance/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/generics-variance/</guid><description>解释泛型的协变/逆变/不变，并用实际例子说明原因。</description></item><item><title>分布式计算的八大谬论：你真的能相信网络吗？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/fallacies-of-distributed-computing/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/fallacies-of-distributed-computing/</guid><description>用工程视角解释分布式计算的常见谬论与风险。</description></item><item><title>分布式系统如何处理故障：超时、重试与降级</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/failure-handling/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/failure-handling/</guid><description>总结分布式系统常见故障处理策略，并给出工程落地建议。</description></item><item><title>封闭网络 vs 开放网络：分布式系统的不同设计重点</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/secure-vs-open-network-architecture/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/secure-vs-open-network-architecture/</guid><description>对比封闭安全网络与开放网络下的架构重点与安全策略。</description></item><item><title>好的语言与差的语言：工程视角的判断标准</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/good-vs-bad-language/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/good-vs-bad-language/</guid><description>从可读性、工具链与安全性出发，讨论语言优劣的工程标准。</description></item><item><title>健壮性原则：发送要保守，接收要开放</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/robustness-principle/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/robustness-principle/</guid><description>解释健壮性原则（Postel 定律）的含义、价值与工程落地方法。</description></item><item><title>紧急设计 vs 演化架构：系统如何在变化中保持方向</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/emergent-vs-evolutionary-architecture/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/emergent-vs-evolutionary-architecture/</guid><description>对比紧急设计与演化架构的理念与工程实践。</description></item><item><title>没有数据库事务时，如何从头实现事务语义</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/implement-transactions-without-db/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/implement-transactions-without-db/</guid><description>讨论无事务系统的补救思路：日志、锁与补偿。</description></item><item><title>什么时候紧耦合是合理的：工程上的现实选择</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/when-tight-coupling-ok/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/when-tight-coupling-ok/</guid><description>讨论紧耦合的合理场景与风险控制方法。</description></item><item><title>什么是 Cloud Ready：系统上云前必须具备的特征</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/cloud-ready-system/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/cloud-ready-system/</guid><description>总结云就绪系统的关键特征与工程实践清单。</description></item><item><title>什么是 Wait-Free 算法：并发中的最高进度保证</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/wait-free-algorithms/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/wait-free-algorithms/</guid><description>解释 wait-free 的含义，与 lock-free/obstruction-free 的区别，并给出实践理解。</description></item><item><title>网络分区后的恢复手段：一致性、对账与补偿</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/network-partitions-recovery/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/network-partitions-recovery/</guid><description>解释网络分区后的常见恢复策略与工程实践。</description></item><item><title>为什么 Git 分支比 SVN 更容易：机制差异与工程影响</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/branching-easier-than-svn/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/branching-easier-than-svn/</guid><description>解释 Git 分支机制与 SVN 的差异，以及对团队协作的影响。</description></item><item><title>为什么很多工程师不喜欢 Java：现实原因与改进路径</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/why-devs-dislike-java/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/why-devs-dislike-java/</guid><description>从工程角度解释“讨厌 Java”的常见理由，并给出理性视角。</description></item><item><title>线程安全单例：如何正确实现与验证</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/thread-safe-singleton/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/thread-safe-singleton/</guid><description>解释线程安全单例的实现方式与常见陷阱。</description></item><item><title>线程饿死（Starvation）：为什么有线程永远拿不到资源</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/starvation-basics/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/starvation-basics/</guid><description>解释线程饿死的成因、影响与工程预防策略。</description></item><item><title>在存储过程中写业务逻辑：优点、缺点与边界</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/business-logic-in-stored-procedures/</link><pubDate>Sat, 24 Jan 2026 13:08:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/business-logic-in-stored-procedures/</guid><description>讨论把业务逻辑放进存储过程的收益、风险与适用场景。</description></item><item><title>CAP 理论怎么落地：CP、AP、CA 的直观例子</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/cap-theorem-examples/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/cap-theorem-examples/</guid><description>用工程场景解释 CAP 理论，并给出系统选型与权衡建议。</description></item><item><title>NoSQL 如何解决可伸缩性挑战</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/nosql-scalability/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/nosql-scalability/</guid><description>从数据模型、分片与一致性角度解释 NoSQL 的扩展能力。</description></item><item><title>函数是第一公民意味着什么：语言设计与工程价值</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/functions-as-first-class/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/functions-as-first-class/</guid><description>解释“函数是第一公民”的含义、收益与代价，并给出工程实践建议。</description></item><item><title>何时选择文档数据库而不是关系型数据库</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/when-document-db/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/when-document-db/</guid><description>对比文档数据库与关系型数据库的优势、限制与选型要点。</description></item><item><title>没有异常机制的语言设计：收益与代价</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/no-exceptions-pros-cons/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/no-exceptions-pros-cons/</guid><description>分析无异常机制的语言设计动机、优点与缺点，并给出工程取舍建议。</description></item><item><title>匿名函数的价值：快速封装与局部表达</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/anonymous-functions-uses/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/anonymous-functions-uses/</guid><description>说明匿名函数的用途、适用场景与常见误区。</description></item><item><title>请求/响应 vs 发布/订阅：什么时候用哪种通信模式</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/request-response-vs-pubsub/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/request-response-vs-pubsub/</guid><description>对比请求/响应与发布/订阅的适用场景、优缺点与选型建议。</description></item><item><title>引用透明性：为什么纯函数让系统更可靠</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/referential-transparency/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/referential-transparency/</guid><description>解释引用透明性与非透明性，给出工程上的收益与边界。</description></item><item><title>最终一致性：分布式系统中的现实与取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/eventual-consistency/</link><pubDate>Sat, 24 Jan 2026 13:04:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/eventual-consistency/</guid><description>解释最终一致性的含义、适用场景与工程实践建议。</description></item><item><title>除了代码之外，我最看重同事的三项素质</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/three-qualities-of-colleagues/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/three-qualities-of-colleagues/</guid><description>从协作与工程文化角度，讨论技术团队中最重要的三项非技术素质。</description></item><item><title>泛型有什么用：复用、安全与表达力</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/generics-why/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/generics-why/</guid><description>解释泛型的价值、适用场景与常见误区。</description></item><item><title>关于代码，我希望非技术同事知道的三件事</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/three-things-nontech-should-know/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/three-things-nontech-should-know/</guid><description>从协作视角解释非技术同事理解代码需要知道的三件事。</description></item><item><title>名字空间（Namespace）有什么用：组织与隔离</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/namespace-why/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/namespace-why/</guid><description>解释名字空间解决的冲突问题，并给出替代方案与实践建议。</description></item><item><title>模式匹配 vs Switch：表达力与可维护性的差异</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/pattern-matching-vs-switch/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/pattern-matching-vs-switch/</guid><description>对比模式匹配与 switch 的表达力、可读性与可维护性，并给出实践建议。</description></item><item><title>如何在不加薪的情况下留住团队成员：成长、认可与环境</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/persuade-team-not-to-leave/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/persuade-team-not-to-leave/</guid><description>从成长空间、认可机制与工作环境出发，给出留人策略与实践建议。</description></item><item><title>什么是动态方法调度：运行时多态的机制</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/dynamic-method-dispatch/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/dynamic-method-dispatch/</guid><description>解释动态方法调度的概念、作用与常见实现方式。</description></item><item><title>什么是高阶函数：概念、用途与示例</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/higher-order-functions/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/higher-order-functions/</guid><description>解释高阶函数的定义、常见用途与工程实践示例。</description></item><item><title>什么是栈与堆：内存模型的关键区别</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/stack-vs-heap/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/stack-vs-heap/</guid><description>从生命周期、分配方式与性能出发解释栈与堆的区别，并给出工程建议。</description></item><item><title>为什么要自动化：节省时间与降低错误</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/automate-your-workflow/</link><pubDate>Sat, 24 Jan 2026 12:53:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/automate-your-workflow/</guid><description>说明自动化对工程效率与质量的价值，并给出实践建议。</description></item><item><title>会议太多怎么办：减少噪音、提升产出</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/too-many-meetings/</link><pubDate>Sat, 24 Jan 2026 12:50:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/too-many-meetings/</guid><description>给出减少会议负担的实用策略，并提高协作效率。</description></item><item><title>如何管理弹性工作制团队：信任、结果与节奏</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/flexible-work-team/</link><pubDate>Sat, 24 Jan 2026 12:50:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/flexible-work-team/</guid><description>给出弹性工作制与按需休假团队的管理实践与风险控制方法。</description></item><item><title>如何管理高流动团队：知识沉淀与稳定交付</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/high-turnover-team/</link><pubDate>Sat, 24 Jan 2026 12:50:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/high-turnover-team/</guid><description>给出管理高流动团队的工程策略，强调知识沉淀与流程稳定。</description></item><item><title>向 CEO 解释看板：为什么值得投资</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/kanban-explained-to-ceo/</link><pubDate>Sat, 24 Jan 2026 12:50:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/kanban-explained-to-ceo/</guid><description>用商业视角解释看板的价值与投入产出，适合向管理层沟通。</description></item><item><title>项目长期延期怎么办：诊断、止血与重启</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/long-delayed-project/</link><pubDate>Sat, 24 Jan 2026 12:50:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/long-delayed-project/</guid><description>给出项目延期的诊断方法与可执行的止血策略。</description></item><item><title>Greenfield vs Brownfield：新项目还是老系统？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/greenfield-vs-brownfield/</link><pubDate>Sat, 24 Jan 2026 12:47:45 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/greenfield-vs-brownfield/</guid><description>对比新建项目与遗留系统的利弊，并给出选择与落地策略。</description></item><item><title>敏捷 vs 瀑布：最大差异与适用场景</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/agile-vs-waterfall/</link><pubDate>Sat, 24 Jan 2026 12:47:45 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/agile-vs-waterfall/</guid><description>对比敏捷与瀑布的核心差异，并给出选型建议。</description></item><item><title>如何处理遗留代码：安全改动与渐进式重构</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/legacy-code-management/</link><pubDate>Sat, 24 Jan 2026 12:47:45 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/legacy-code-management/</guid><description>给出处理遗留代码的工程策略，包括测试保护网与渐进式改造。</description></item><item><title>什么是敏捷（Agile）：原则、实践与误区</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/agile-basics/</link><pubDate>Sat, 24 Jan 2026 12:47:45 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/agile-basics/</guid><description>解释敏捷的核心原则、常见实践与落地误区。</description></item><item><title>为什么软件维护困难：复杂性、耦合与人</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/why-software-maintenance-hard/</link><pubDate>Sat, 24 Jan 2026 12:47:45 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/why-software-maintenance-hard/</guid><description>分析软件维护困难的原因，并给出缓解策略。</description></item><item><title>CLIP 系列（1/3）：原理与对比学习公式——多模态对齐的核心机制</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/clip/1-clip-principles-and-contrastive-learning/</link><pubDate>Sat, 24 Jan 2026 12:46:49 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/clip/1-clip-principles-and-contrastive-learning/</guid><description>用数学公式拆解 CLIP 的对比学习目标、嵌入空间与训练流程，建立可复用的多模态理解框架。</description></item><item><title>CLIP 系列（2/3）：PyTorch 完整可复现实战——从数据到训练闭环</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/clip/2-clip-pytorch-reproducible-implementation/</link><pubDate>Sat, 24 Jan 2026 12:46:49 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/clip/2-clip-pytorch-reproducible-implementation/</guid><description>用 CIFAR-10 + 文本提示搭建最小 CLIP 训练闭环，提供完整可复现的 PyTorch 实战脚本。</description></item><item><title>CLIP 系列（3/3）：工程化与优化——检索、索引与部署实践</title><link>https://jeanphilogong.github.io/jeanblog/zh/ai/clip/3-clip-engineering-and-optimization/</link><pubDate>Sat, 24 Jan 2026 12:46:49 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/ai/clip/3-clip-engineering-and-optimization/</guid><description>围绕 CLIP 的工程落地，总结向量索引、批量推理与性能优化的实践路线。</description></item><item><title>好莱坞原则：别打给我，我会打给你</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/hollywood-principle/</link><pubDate>Sat, 24 Jan 2026 12:45:38 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/hollywood-principle/</guid><description>解释好莱坞原则的含义、与 IoC 的关系，以及在框架设计中的落地方式。</description></item><item><title>为什么人们会抵制变化：心理成本与组织摩擦</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/management/why-people-resist-change/</link><pubDate>Sat, 24 Jan 2026 12:45:38 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/management/why-people-resist-change/</guid><description>从心理学与组织机制解释抵制变化的原因，并给出落地的推动策略。</description></item><item><title>为什么说 goto 有害：可读性、可维护性与替代方案</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/why-goto-harmful/</link><pubDate>Sat, 24 Jan 2026 12:45:38 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/why-goto-harmful/</guid><description>分析 goto 带来的问题，并给出可读性更好的替代写法。</description></item><item><title>下一步该自动化什么：识别高收益自动化机会</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/what-to-automate-next/</link><pubDate>Sat, 24 Jan 2026 12:45:38 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/what-to-automate-next/</guid><description>给出识别自动化机会的工程方法，包括频率、成本与风险维度。</description></item><item><title>依赖地狱（Dependency Hell）怎么解：版本、隔离与治理</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/dependency-hell/</link><pubDate>Sat, 24 Jan 2026 12:45:38 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/dependency-hell/</guid><description>从版本冲突与传递依赖出发，给出依赖地狱的治理方法与工具策略。</description></item><item><title>测试如何影响软件设计：可测试性驱动的结构选择</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/testing-in-design/</link><pubDate>Sat, 24 Jan 2026 12:42:52 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/testing-in-design/</guid><description>从可测试性出发解释测试如何影响设计决策，并给出可落地的结构建议。</description></item><item><title>代码中的注释有用吗：什么时候写、写什么</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/comments-in-code/</link><pubDate>Sat, 24 Jan 2026 12:42:52 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/comments-in-code/</guid><description>解释注释的价值与风险，并给出可执行的注释策略。</description></item><item><title>内聚与耦合的区别：衡量设计质量的两把尺</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/cohesion-vs-coupling/</link><pubDate>Sat, 24 Jan 2026 12:42:52 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/cohesion-vs-coupling/</guid><description>解释内聚与耦合的差异，给出评估与改进方法。</description></item><item><title>为什么 TDD 先写测试：反馈、设计与信心</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/tdd-tests-first/</link><pubDate>Sat, 24 Jan 2026 12:42:52 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/tdd-tests-first/</guid><description>解释 TDD 先写测试的原因，以及它如何改善设计与反馈速度。</description></item><item><title>重构何时有用：时机、信号与风险控制</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/refactoring-when/</link><pubDate>Sat, 24 Jan 2026 12:42:52 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/refactoring-when/</guid><description>解释重构适用场景、触发信号与风险控制方法。</description></item><item><title>Hot100：缺失的第一个正数（First Missing Positive）原地索引定位 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/41-first-missing-positive/</link><pubDate>Sat, 24 Jan 2026 12:42:31 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/41-first-missing-positive/</guid><description>用原地索引定位在 O(n) 时间、O(1) 额外空间找到缺失的第一个正数，含工程场景与多语言实现。</description></item><item><title>Active Record vs Data Mapper：差异、优缺点与选型</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/active-record-vs-data-mapper/</link><pubDate>Sat, 24 Jan 2026 12:40:32 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/active-record-vs-data-mapper/</guid><description>对比 Active Record 与 Data Mapper 两种持久化模式的适用场景与代价。</description></item><item><title>迪米特法则（最少知识原则）：违例与修复示例</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/law-of-demeter/</link><pubDate>Sat, 24 Jan 2026 12:40:32 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/law-of-demeter/</guid><description>解释迪米特法则的核心思想，并给出违反与修复示例。</description></item><item><title>空对象模式的目的：消除空指针分支</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/null-object-pattern/</link><pubDate>Sat, 24 Jan 2026 12:40:32 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/null-object-pattern/</guid><description>解释空对象模式的设计目的、适用场景与工程落地示例。</description></item><item><title>全局对象为何危险：隐藏依赖与测试失控</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/global-object-anti-pattern/</link><pubDate>Sat, 24 Jan 2026 12:40:32 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/global-object-anti-pattern/</guid><description>通过示例说明全局对象的危害，并给出替代方案。</description></item><item><title>为什么组合优于继承：灵活性、可测试性与演进成本</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/composition-over-inheritance/</link><pubDate>Sat, 24 Jan 2026 12:40:32 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design-patterns/composition-over-inheritance/</guid><description>解释“组合优于继承”的工程原因，并给出可运行示例与落地步骤。</description></item><item><title>Hot100：除自身以外数组的乘积（Product of Array Except Self）前后缀乘积 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/238-product-of-array-except-self/</link><pubDate>Sat, 24 Jan 2026 12:35:09 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/238-product-of-array-except-self/</guid><description>O(n) 时间、无需除法，用前后缀乘积求解除自身以外数组的乘积，含工程场景与多语言实现。</description></item><item><title>第一方 Cookie vs 第三方 Cookie：差异、风险与政策</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/network/first-party-vs-third-party-cookies/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/network/first-party-vs-third-party-cookies/</guid><description>解释第一方与第三方 Cookie 的区别、隐私风险与浏览器策略。</description></item><item><title>如何维护单体架构：模块化、边界与演进</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/monolith-maintenance/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/monolith-maintenance/</guid><description>给出单体架构的维护策略，强调模块化与边界控制。</description></item><item><title>设计、架构、功能与美学：它们分别解决什么问题？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/design-architecture-functionality-aesthetic/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/design-architecture-functionality-aesthetic/</guid><description>用工程视角区分设计、架构、功能与美学，并给出落地实践与协作方式。</description></item><item><title>什么是闭包：概念、用途与类的相似点</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/closures-and-uses/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/closures-and-uses/</guid><description>解释闭包的概念、常见用途，并对比闭包与类的相似点。</description></item><item><title>什么是好代码：可读、可测、可演进</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/what-is-good-code/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/what-is-good-code/</guid><description>从可读性、正确性与演进成本出发定义好代码，并给出可执行的判断清单。</description></item><item><title>什么是双因素认证（2FA）：机制、实现与风险</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/security/two-factor-auth-basics/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/security/two-factor-auth-basics/</guid><description>解释 2FA 的基本机制、常见实现方式与工程注意事项。</description></item><item><title>什么是专业的开发者：责任、质量与协作</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/what-is-professional-developer/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/what-is-professional-developer/</guid><description>从责任心、质量意识与协作能力出发，定义专业开发者的标准与实践。</description></item><item><title>为什么大公司创新更慢：结构、风险与激励</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/why-large-companies-slow-innovation/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/why-large-companies-slow-innovation/</guid><description>分析大公司创新变慢的结构性原因，并给出工程层面的改进策略。</description></item><item><title>在已有 Web 应用中实现 2FA：落地步骤与风险控制</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/security/implement-2fa-in-existing-web/</link><pubDate>Sat, 24 Jan 2026 12:33:47 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/security/implement-2fa-in-existing-web/</guid><description>给出在已有 Web 应用引入 2FA 的工程步骤、数据模型与回滚策略。</description></item><item><title>发布/订阅在可扩展性上的缺点：一致性与可观测性成本</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/publish-subscribe-cons/</link><pubDate>Sat, 24 Jan 2026 12:29:51 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/publish-subscribe-cons/</guid><description>分析发布/订阅模型在扩展性方面的缺点与工程成本。</description></item><item><title>如何避免供应商锁定（Vendor Lock-in）：策略与实践</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/vendor-lock-in/</link><pubDate>Sat, 24 Jan 2026 12:29:51 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/vendor-lock-in/</guid><description>给出防止供应商锁定的工程策略，包括抽象层、可迁移架构与数据可移植性。</description></item><item><title>如何设计高可扩展系统：从瓶颈到弹性</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/design-scalable-systems/</link><pubDate>Sat, 24 Jan 2026 12:29:51 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/design-scalable-systems/</guid><description>给出设计高可扩展系统的核心原则与实践步骤。</description></item><item><title>什么是三层架构：职责划分与工程价值</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/threelayer-architecture/</link><pubDate>Sat, 24 Jan 2026 12:29:51 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/threelayer-architecture/</guid><description>解释三层架构的分层职责、优缺点与适用场景。</description></item><item><title>输入网址回车后发生了什么：从 DNS 到渲染</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/network/what-happens-when-you-type-url/</link><pubDate>Sat, 24 Jan 2026 12:29:51 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/network/what-happens-when-you-type-url/</guid><description>系统梳理从输入网址到页面渲染的关键步骤，并给出排查思路。</description></item><item><title>Hot100：轮转数组（Rotate Array）三次反转 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/189-rotate-array/</link><pubDate>Sat, 24 Jan 2026 12:29:42 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/189-rotate-array/</guid><description>用三次反转在 O(n) 时间完成轮转数组，含工程场景、常见误区与多语言实现。</description></item><item><title>C10k 问题怎么解决：高并发连接的策略</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/c10k-strategies/</link><pubDate>Sat, 24 Jan 2026 12:25:49 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/c10k-strategies/</guid><description>解释 C10k 的成因与解决策略，包括事件驱动、内核调优与连接管理。</description></item><item><title>横向扩展 vs 纵向扩展：区别、场景与取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/scale-up-vs-scale-out/</link><pubDate>Sat, 24 Jan 2026 12:25:49 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/scale-up-vs-scale-out/</guid><description>解释 scale up 与 scale out 的差异、适用场景与成本模型。</description></item><item><title>什么是 CQRS：命令与查询职责分离</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/cqrs-basics/</link><pubDate>Sat, 24 Jan 2026 12:25:49 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/cqrs-basics/</guid><description>解释 CQRS 的核心思想、适用场景与落地成本。</description></item><item><title>性能与可扩展性的关系：别把快当成能扩</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/performance-vs-scalability/</link><pubDate>Sat, 24 Jan 2026 12:25:49 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/performance-vs-scalability/</guid><description>解释性能与可扩展性的差异与关系，并给出架构决策与指标建议。</description></item><item><title>从 MySQL 迁移到 PostgreSQL：步骤、风险与检查清单</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/mysql-to-postgresql-migration/</link><pubDate>Sat, 24 Jan 2026 12:23:30 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/mysql-to-postgresql-migration/</guid><description>给出 MySQL 迁移到 PostgreSQL 的工程化步骤、风险点与回滚策略。</description></item><item><title>缓存什么时候危险：一致性、失效与业务风险</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/caching-when-dangerous/</link><pubDate>Sat, 24 Jan 2026 12:23:30 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/caching-when-dangerous/</guid><description>说明缓存何时会带来风险，并给出适用条件与规避策略。</description></item><item><title>如何测试分布式系统：故障注入与一致性验证</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/distributed-testing/</link><pubDate>Sat, 24 Jan 2026 12:23:30 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/distributed-testing/</guid><description>从故障注入、时钟偏移、网络分区出发，给出分布式系统测试的工程方法。</description></item><item><title>数据库 Schema 迁移怎么做：安全、可回滚、可验证</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/schema-migration/</link><pubDate>Sat, 24 Jan 2026 12:23:30 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/schema-migration/</guid><description>从灰度、回滚与数据一致性角度讲清数据库 schema 迁移的工程实践。</description></item><item><title>远程过程调用（RPC）的通用缺点：成本与风险</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/rpc-common-drawbacks/</link><pubDate>Sat, 24 Jan 2026 12:23:30 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/rpc-common-drawbacks/</guid><description>总结 RPC 的通用缺点，包括耦合、可观测性与错误处理复杂度。</description></item><item><title>如何找出最耗时的查询：从日志到指标</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/slow-query-identification/</link><pubDate>Sat, 24 Jan 2026 11:28:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/slow-query-identification/</guid><description>给出定位慢查询的工程路径：日志、慢查询表、APM 与索引分析。</description></item><item><title>什么是 ACID：事务的四个核心属性</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/acid-basics/</link><pubDate>Sat, 24 Jan 2026 11:28:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/acid-basics/</guid><description>解释 ACID 的四个属性及其工程意义，并给出常见误区与实践建议。</description></item><item><title>什么是 N+1 查询问题：成因、检测与优化</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/n-plus-one-problem/</link><pubDate>Sat, 24 Jan 2026 11:28:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/n-plus-one-problem/</guid><description>解释 ORM 常见的 N+1 问题，提供检测思路与优化策略。</description></item><item><title>为什么 SQL 中 NULL 不能用 = 比较：三值逻辑与查询陷阱</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/why-null-not-equal/</link><pubDate>Sat, 24 Jan 2026 11:28:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/why-null-not-equal/</guid><description>解释 SQL 三值逻辑下 NULL 的比较规则，并给出正确写法与常见陷阱。</description></item><item><title>延迟加载（Lazy Loading）是什么：适用场景与代价</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/lazy-loading/</link><pubDate>Sat, 24 Jan 2026 11:28:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/lazy-loading/</guid><description>解释延迟加载的工作方式、适用场景与常见缺陷，并给出实践建议。</description></item><item><title>Hot100：合并区间（Merge Intervals）排序扫描 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/56-merge-intervals/</link><pubDate>Sat, 24 Jan 2026 11:21:03 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/56-merge-intervals/</guid><description>围绕合并区间（LeetCode 56）讲清排序+线性扫描的核心思路、工程场景与多语言实现。</description></item><item><title>GitFlow vs GitHub Flow：工作流差异与选择</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/gitflow-vs-githubflow/</link><pubDate>Sat, 24 Jan 2026 11:20:31 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/gitflow-vs-githubflow/</guid><description>对比 GitFlow 与 GitHub Flow 的适用场景、优劣势与团队实践建议。</description></item><item><title>Web 与桌面应用的容错管理差异：思路与实践</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/fault-tolerance-web-vs-desktop/</link><pubDate>Sat, 24 Jan 2026 11:20:31 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/architecture/fault-tolerance-web-vs-desktop/</guid><description>对比 Web 与桌面应用的容错管理差异，给出关键策略与实践指南。</description></item><item><title>分布式 vs 集中式版本控制：优势、劣势与适用场景</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/distributed-vs-centralized-vcs/</link><pubDate>Sat, 24 Jan 2026 11:20:31 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/distributed-vs-centralized-vcs/</guid><description>对比分布式版本控制（Git）与集中式版本控制（SVN）的优劣势，并给出选型建议。</description></item><item><title>什么时候使用异步通信：场景、收益与代价</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/async-communication-when/</link><pubDate>Sat, 24 Jan 2026 11:20:31 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/distributed/async-communication-when/</guid><description>解释同步/异步通信的取舍，并给出适用场景与落地建议。</description></item><item><title>什么是 rebase：作用、风险与正确用法</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/git-rebase-basics/</link><pubDate>Sat, 24 Jan 2026 11:20:31 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/version-control/git-rebase-basics/</guid><description>解释 Git rebase 的作用、风险与适用场景，并给出安全使用建议。</description></item><item><title>什么是竞争条件：Race Condition 的本质与示例</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/race-condition-basics/</link><pubDate>Sat, 24 Jan 2026 11:11:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/race-condition-basics/</guid><description>用可运行示例解释竞态条件的形成原因，并给出工程层面的规避策略。</description></item><item><title>什么是死锁：成因、示例与规避策略</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/deadlock-basics-general/</link><pubDate>Sat, 24 Jan 2026 11:11:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/deadlock-basics-general/</guid><description>用可运行示例解释死锁的成因，并给出工程上的规避策略。</description></item><item><title>为什么并发测试很难：非确定性与时序爆炸</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/testing-concurrency-is-hard/</link><pubDate>Sat, 24 Jan 2026 11:11:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/testing-concurrency-is-hard/</guid><description>从非确定性、时序组合爆炸与可复现性角度解释并发测试的难点，并给出缓解策略。</description></item><item><title>为什么不该自己设计密码学：风险、误区与替代方案</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/security/why-not-roll-your-own-crypto/</link><pubDate>Sat, 24 Jan 2026 11:11:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/security/why-not-roll-your-own-crypto/</guid><description>解释自创密码学为何高风险，并给出工程上更安全的替代做法。</description></item><item><title>为什么需要并发：吞吐、延迟与资源利用率</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/why-concurrency/</link><pubDate>Sat, 24 Jan 2026 11:11:28 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/concurrency/why-concurrency/</guid><description>从 I/O 等待、吞吐与响应时间出发解释并发的必要性，并给出可运行示例与落地建议。</description></item><item><title>不变性为何让代码更安全：Immutability 的价值与边界</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/immutability-safety/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/immutability-safety/</guid><description>从副作用与共享状态风险出发，解释不变性如何提升代码安全性，并给出可落地实践。</description></item><item><title>缓存大小如何确定：命中率、成本与稳定性</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/system/cache-sizing-principles/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/system/cache-sizing-principles/</guid><description>从命中率、成本与一致性风险出发，给出缓存大小的工程化确定方法与示例。</description></item><item><title>可变值 vs 不可变值：优缺点、成本与工程选择</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/mutable-vs-immutable-tradeoffs/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/mutable-vs-immutable-tradeoffs/</guid><description>从性能、可维护性与并发安全出发，系统对比可变与不可变值的优缺点，并给出选型建议。</description></item><item><title>如何在不可靠协议上构建可靠通信：重传、确认与顺序</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/network/reliable-over-unreliable-protocol/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/network/reliable-over-unreliable-protocol/</guid><description>用确认、超时与重传在不可靠网络上实现可靠通信，并给出简化的可运行示例。</description></item><item><title>什么是 O/R 阻抗失衡：对象世界与关系模型的冲突</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/database/object-relational-impedance-mismatch/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/database/object-relational-impedance-mismatch/</guid><description>解释对象模型与关系模型的不匹配来源，并给出实际工程中的缓解策略。</description></item><item><title>什么是流式处理（Streaming）：概念与实现方式</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/system/streaming-basics/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/system/streaming-basics/</guid><description>解释流式处理的核心概念与实现方式，并给出可运行的最小示例。</description></item><item><title>什么是实时系统：与普通系统的关键区别</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/system/real-time-systems-basics/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/system/real-time-systems-basics/</guid><description>从时限约束与确定性出发，解释实时系统与普通系统的区别，并给出落地实践与示例。</description></item><item><title>实时语言与堆内存分配：为什么动态分配会破坏实时性</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/system/real-time-language-heap-allocation/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/system/real-time-language-heap-allocation/</guid><description>解释实时系统为什么回避堆分配与 GC，并给出可落地的内存管理策略。</description></item><item><title>为什么打开 TCP 套接字开销大：握手、状态与系统成本</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/network/tcp-socket-overhead/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/network/tcp-socket-overhead/</guid><description>从握手、内核状态、慢启动与系统调用角度解释 TCP 连接开销，并给出优化策略。</description></item><item><title>为什么写软件很难：不确定性、复杂性与人</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/why-software-is-hard/</link><pubDate>Sat, 24 Jan 2026 11:06:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/engineering/why-software-is-hard/</guid><description>从需求变化、复杂性与协作成本出发解释软件开发的困难，并给出可落地的缓解策略。</description></item><item><title>CSR vs SSR：取舍、性能指标与落地路径</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/frontend/csr-vs-ssr-tradeoffs/</link><pubDate>Sat, 24 Jan 2026 11:03:05 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/frontend/csr-vs-ssr-tradeoffs/</guid><description>从 TTFB/TTI/SEO 等指标出发，对比 CSR 与 SSR 的优缺点，并给出决策指南与可运行示例。</description></item><item><title>TCP 与 HTTP 的区别：分层、语义与选型</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/network/tcp-vs-http/</link><pubDate>Sat, 24 Jan 2026 11:03:05 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/network/tcp-vs-http/</guid><description>从协议分层和语义角度解释 TCP 与 HTTP 的区别，并给出排查与选型建议。</description></item><item><title>封装为什么重要：边界、演进与可维护性</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/design/encapsulation-importance/</link><pubDate>Sat, 24 Jan 2026 11:03:05 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/design/encapsulation-importance/</guid><description>从系统演进角度解释封装的价值，并给出可落地的封装实践与示例。</description></item><item><title>空引用为何危险：Null Reference 的问题与移除代价</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/null-reference-abolition/</link><pubDate>Sat, 24 Jan 2026 11:03:05 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/null-reference-abolition/</guid><description>从空引用带来的错误谈起，分析移除 null 的收益与代价，并给出可落地的替代方案。</description></item><item><title>为什么函数式编程重要：适用场景与落地路径</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/language/why-functional-programming/</link><pubDate>Sat, 24 Jan 2026 11:03:05 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/language/why-functional-programming/</guid><description>从纯函数与不可变性出发，说明函数式编程的价值、适用场景与渐进式落地方法。</description></item><item><title>LeetCode 42：一张高度图能接住多少雨水？</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/42-trapping-rain-water/</link><pubDate>Sat, 24 Jan 2026 10:27:35 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/42-trapping-rain-water/</guid><description>从单个位置出发，推导边界数组和 O(1) 空间的双指针解法，再把同一个边界模型迁移到单调栈。</description></item><item><title>固定间距 1 检测：一次扫描判断 1 之间至少 k 个间隔（LeetCode 1437）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1437-check-if-all-ones-are-at-least-k-places-away/</link><pubDate>Thu, 22 Jan 2026 09:12:14 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1437-check-if-all-ones-are-at-least-k-places-away/</guid><description>用一次扫描判断所有 1 之间是否至少相隔 k 个位置，含工程场景、常见误区与多语言实现。</description></item><item><title>判断一个数是否为 2 的幂（Power of Two）：位运算 O(1) ACERS 解析（LeetCode 231）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/231-power-of-two/</link><pubDate>Wed, 21 Jan 2026 13:22:53 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/231-power-of-two/</guid><description>用位运算 O(1) 判断整数是否为 2 的幂，含工程场景、常见误区与多语言实现。</description></item><item><title>LeetCode 76：最小覆盖子串（Minimum Window Substring）滑动窗口 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/76-minimum-window-substring/</link><pubDate>Tue, 20 Jan 2026 21:59:19 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/76-minimum-window-substring/</guid><description>用可变滑动窗口与计数哈希表在 O(n) 时间找到最小覆盖子串，含工程场景与多语言实现。</description></item><item><title>LeetCode 1456：最大元音子串数量的滑动窗口 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1456-maximum-number-of-vowels-in-a-substring-of-given-length/</link><pubDate>Tue, 20 Jan 2026 13:40:45 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1456-maximum-number-of-vowels-in-a-substring-of-given-length/</guid><description>用固定滑动窗口在 O(n) 时间求最大元音子串数量，含工程迁移与多语言实现。</description></item><item><title>Go 死锁排查 Checklist：从报错到定位的实用手册</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/go/go-deadlock-checklist/</link><pubDate>Mon, 19 Jan 2026 12:34:37 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/go/go-deadlock-checklist/</guid><description>整理一套可执行的 Go 死锁排查清单，覆盖日志、堆栈、协程状态与常见根因快速定位。</description></item><item><title>Go 死锁入门：常见场景、排查方法与工程实践</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/go/go-deadlock-basics/</link><pubDate>Mon, 19 Jan 2026 11:33:39 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/go/go-deadlock-basics/</guid><description>一篇面向新手的 Go 死锁入门指南，讲清死锁本质、常见触发场景、排查路径与工程规避策略。</description></item><item><title>LeetCode 1513：仅含 1 的子串数量（连续 1 子串计数）ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1513-number-of-substrings-with-only-ones/</link><pubDate>Sun, 18 Jan 2026 18:42:59 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1513-number-of-substrings-with-only-ones/</guid><description>用连续段在线统计在 O(n) 时间计算仅含 1 的子串数量，含工程场景与多语言实现。</description></item><item><title>LeetCode 239：滑动窗口最大值</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/239-sliding-window-maximum/</link><pubDate>Sun, 18 Jan 2026 18:32:08 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/239-sliding-window-maximum/</guid><description>从完整窗口的边界出发，先实现正确的扫描解法，再推导出 O(n) 的索引双端队列解法。</description></item><item><title>Hot100：和为 K 的子数组（Subarray Sum Equals K）前缀和 + 哈希表 ACERS 解析</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/560-subarray-sum-equals-k/</link><pubDate>Sat, 17 Jan 2026 20:49:25 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/560-subarray-sum-equals-k/</guid><description>用前缀和与哈希表在 O(n) 时间统计和为 K 的子数组数量，含工程场景、多语言实现与常见误区。</description></item><item><title>Git 入门教程：从零开始管理代码版本</title><link>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-basics-getting-started/</link><pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-basics-getting-started/</guid><description>&lt;h3 id="标题"&gt;&lt;strong&gt;标题&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Git 入门教程：从零开始管理代码版本&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="副标题--摘要"&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;一篇面向新手的 Git 基础使用指南，从初始化仓库、提交版本到远程协作，
用最少命令完成日常开发流转。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="目标读者"&gt;&lt;strong&gt;目标读者&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;初学者&lt;/strong&gt;：第一次接触 Git，希望快速上手基本命令。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;转岗工程师&lt;/strong&gt;：从单机开发转为团队协作，需要熟悉版本管理流程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学生&lt;/strong&gt;：做课程项目或实验，需要规范保存代码历史。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="背景--动机"&gt;&lt;strong&gt;背景 / 动机&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;没有版本管理时，常见的做法是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“先复制一份目录，改完再看看哪个好用。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这种方式很快会失控：文件版本混乱、无法回退、多人协作冲突频发。
Git 的价值在于&lt;strong&gt;记录每一次变更&lt;/strong&gt;，让你随时回到过去的任意状态，
并支持多人同时开发而不互相覆盖。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="核心概念"&gt;&lt;strong&gt;核心概念&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;仓库（Repository）&lt;/strong&gt;：一个包含代码与历史记录的目录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作区（Working Directory）&lt;/strong&gt;：你当前编辑的文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;暂存区（Staging Area）&lt;/strong&gt;：等待提交的文件清单。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提交（Commit）&lt;/strong&gt;：一次可追溯的版本快照。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;远程仓库（Remote）&lt;/strong&gt;：托管在服务器上的仓库，用于协作和备份。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="实践指南--步骤"&gt;&lt;strong&gt;实践指南 / 步骤&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1️⃣ &lt;strong&gt;初始化仓库&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git init
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;2️⃣ &lt;strong&gt;查看当前状态&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;3️⃣ &lt;strong&gt;把文件加入暂存区&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;4️⃣ &lt;strong&gt;提交一次版本&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git commit -m &lt;span style="color:#e6db74"&gt;&amp;#34;init: first commit&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;5️⃣ &lt;strong&gt;绑定远程仓库并推送&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git remote add origin https://example.com/your/repo.git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git branch -M main
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git push -u origin main
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="协作流程从克隆到提交"&gt;&lt;strong&gt;协作流程（从克隆到提交）&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;这一部分是团队协作的核心，决定了你能否安全、稳定地和他人同步代码。
掌握这些命令，能避免覆盖同事的提交，减少冲突和返工。&lt;/p&gt;</description></item><item><title>任务编排为什么要放后端：让流程可控、可变、可回放</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pipeline-orchestration/</link><pubDate>Tue, 13 Jan 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pipeline-orchestration/</guid><description>用通俗语言解释“后端任务编排”的必要性：把流程顺序从前端移到后端，用配置驱动步骤与等待点。给出最小可运行示例、落地步骤与常见坑。</description></item><item><title>算法与业务的关系：把不确定性变成可交付（以 LLM 事实抽取为例）</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/algorithm-vs-business/</link><pubDate>Wed, 07 Jan 2026 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/algorithm-vs-business/</guid><description>用投标写作系统为例，解释算法与业务的边界：算法负责把模糊世界压缩成结构化中间态，业务负责在中间态稳定后编排流程。附：JupyterLab 验证清单、最小可运行示例与常见坑。</description></item><item><title>size_t 有什么用？为什么 C++ 循环更偏爱 size_t 而不是 int</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/c++/size_t-why-not-int-loop/</link><pubDate>Tue, 30 Dec 2025 18:12:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/c++/size_t-why-not-int-loop/</guid><description>&lt;h1 id="size_"&gt;&lt;strong&gt;size_t 有什么用？为什么 C++ 循环更偏爱 size_t 而不是 int&lt;/strong&gt;&lt;/h1&gt;
&lt;h3 id="副标题--摘要"&gt;副标题 / 摘要&lt;/h3&gt;
&lt;p&gt;当你写 &lt;code&gt;for&lt;/code&gt; 循环遍历容器时，&lt;code&gt;size_t&lt;/code&gt; 往往比 &lt;code&gt;int&lt;/code&gt; 更安全、更贴合语义。本文用 ACERS 结构讲清楚 &lt;code&gt;size_t&lt;/code&gt; 的定义、使用理由、风险点与工程实践，适合写 C++ 的你快速落地。&lt;/p&gt;
&lt;h3 id="元信息"&gt;元信息&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;阅读时长：8-10 分钟&lt;/li&gt;
&lt;li&gt;标签：C++，size_t，类型系统，循环，STL&lt;/li&gt;
&lt;li&gt;SEO 关键词：size_t 用途，size_t 和 int 区别，C++ 循环初始化，size_t 下溢&lt;/li&gt;
&lt;li&gt;元描述：解释 size_t 的定义与用途，说明为什么循环索引常用 size_t，并给出安全写法与工程场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="目标读者"&gt;目标读者&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;C++ 初学者：对 &lt;code&gt;size_t&lt;/code&gt;、&lt;code&gt;sizeof&lt;/code&gt;、容器 &lt;code&gt;size()&lt;/code&gt; 的返回类型不熟悉&lt;/li&gt;
&lt;li&gt;中级工程师：遇到过 &lt;code&gt;-Wsign-compare&lt;/code&gt; 警告或下溢 bug&lt;/li&gt;
&lt;li&gt;需要写跨平台/高性能 C++ 代码的人&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="背景--动机"&gt;背景 / 动机&lt;/h3&gt;
&lt;p&gt;在 C++ 代码里，你经常能看到这样的循环：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (size_t i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; vec.size(); &lt;span style="color:#f92672"&gt;++&lt;/span&gt;i) { ... }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不少人疑惑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为什么不用更“直观”的 &lt;code&gt;int&lt;/code&gt;？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;size_t&lt;/code&gt; 到底是什么？为什么是无符号？&lt;/li&gt;
&lt;li&gt;什么时候会踩坑？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这一篇把这些问题一次讲清楚。&lt;/p&gt;</description></item><item><title>数据结构基础：好数对计数（Number of Good Pairs）哈希统计 ACERS 解析（LeetCode 1512）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1512-number-of-good-pairs/</link><pubDate>Tue, 30 Dec 2025 11:40:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/1512-number-of-good-pairs/</guid><description>用哈希计数一遍扫描解决好数对（Good Pairs）问题，附工程场景、复杂度对比与多语言实现。</description></item><item><title>Hot100：Two Sum 两数之和哈希表一遍扫描与 ACERS 工程化解析（LeetCode 1）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/1-two-sum/</link><pubDate>Sun, 28 Dec 2025 10:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/1-two-sum/</guid><description>从题意还原到哈希表一遍扫描，系统讲解 Two Sum，并给出工程场景、复杂度对比与多语言实现。</description></item><item><title>XOR 与 RC5：从原理到 Go 实战（含安全替代建议）</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/go/xor-rec4-primer/</link><pubDate>Tue, 16 Dec 2025 18:12:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/go/xor-rec4-primer/</guid><description>面向 Go/后端工程师的 XOR 与 RC4 入门与实践，包含可运行示例与安全替代建议。</description></item><item><title>Python 抽象基类 ABC vs ABCMeta：什么时候用哪个？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/abc-vs-abcmeta-hugo/</link><pubDate>Fri, 12 Dec 2025 23:52:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/abc-vs-abcmeta-hugo/</guid><description>用可运行示例讲清 ABC 与 ABCMeta 的区别：一个是便捷基类，一个是元类；分别适合接口约束与类创建期的自动注入/校验。</description></item><item><title>从参数直传到Pipeline: 一次可复现可观测的数据处理管线改造实践</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/from-direct-params-to-config-driven-etl-pipeline/</link><pubDate>Wed, 10 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/from-direct-params-to-config-driven-etl-pipeline/</guid><description>&lt;h1 id="从参数直传到-pipeline一次可复现可观测的数据处理管线改造实践"&gt;从参数直传到 Pipeline：一次可复现、可观测的数据处理管线改造实践&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题：&lt;/strong&gt; 为什么当处理链变得越来越长时，“配置驱动 + 上下文 + 外部存储”的 Pipeline 模式会比“参数直传”更适合工作？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;标签：&lt;/strong&gt; Python / Pipeline / ETL / 数据工程 / 架构设计
&lt;strong&gt;适读人群：&lt;/strong&gt; 后端开发、数据工程师、做文档处理/Embedding/索引构建的同学
&lt;strong&gt;阅读时间：&lt;/strong&gt; 10–15 min
&lt;strong&gt;摘要：&lt;/strong&gt; 本文记录我从“领域模型传来传去”的后端式写法，迁移到“配置驱动 Pipeline”模式的过程，总结落地要点、踩过的坑，以及为什么这种模式更适合复杂的数据加工链。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="-写这篇文章的动机"&gt;🧭 写这篇文章的动机&lt;/h1&gt;
&lt;p&gt;做后端时，我长期习惯一种简单粗暴的风格：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;需要什么参数就一直往下传，函数链一路 call 下去。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;很多业务都是这么写的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入是个模型/DTO&lt;/li&gt;
&lt;li&gt;处理完传下一个函数&lt;/li&gt;
&lt;li&gt;大对象在链路里飘来飘去&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但当我开始做 &lt;strong&gt;文档处理、Embedding、实体提取、索引构建&lt;/strong&gt; 这种“多步骤、可重跑、需观测”的任务时，这种写法很快失效：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要重跑某一步时必须重建整条调用链&lt;/li&gt;
&lt;li&gt;中间产物无法落地检查&lt;/li&gt;
&lt;li&gt;改一个策略需要改一堆函数签名&lt;/li&gt;
&lt;li&gt;并发 / 异常恢复都难处理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;后来接触到构建数据处理 Workflow / ETL Pipeline 的方式，发现它的核心思路完全不一样：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;配置驱动策略 → 上下文承载运行态 → 外部存储承载数据流。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这套体系让多步处理链突然变得可插拔、能恢复、能观测、能重放。
于是就有了这篇文章，把我的心智迁移过程与实践要点记录下来。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="-tldr你只看这一屏也能理解本文核心"&gt;⚡ TL;DR（你只看这一屏也能理解本文核心）&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;配置驱动：&lt;/strong&gt; 路径、模型、超参都写 config，而不是塞进函数参数里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;上下文 context：&lt;/strong&gt; 统一管理 I/O 句柄、缓存、回调、统计、运行时标志。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外部存储：&lt;/strong&gt; 步骤间不传大对象，读写约定表名：&lt;code&gt;documents → text_units → entities → index&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可插拔 Pipeline：&lt;/strong&gt; “步骤名 → 函数指针”的顺序列表，可一键切换 Standard/Fast 等方案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;幂等与恢复：&lt;/strong&gt; 中间表持久化，可覆盖或版本化，崩溃后能断点续跑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;观测与回调：&lt;/strong&gt; start/end/进度统一上报，产出 stats.json，定位问题更快。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异步友好：&lt;/strong&gt; 步骤 await 执行，内部可分片并发或调用 LLM。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;取舍：&lt;/strong&gt; 成本是心智负担增加；收益是可观察、可重跑、可替换、低耦合。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1 id="-目标读者"&gt;👥 目标读者&lt;/h1&gt;
&lt;p&gt;适合以下同学阅读：&lt;/p&gt;</description></item><item><title>Pydantic vs dataclass vs TypedDict：谁负责什么，怎么组合？</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pydantic-vs-dataclass-vs-typeddict/</link><pubDate>Tue, 09 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pydantic-vs-dataclass-vs-typeddict/</guid><description>&lt;h3 id="标题"&gt;标题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Pydantic vs dataclass vs TypedDict：谁负责什么，怎么组合？&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="副标题--摘要"&gt;副标题 / 摘要&lt;/h3&gt;
&lt;p&gt;承接《别让 Pydantic 占领你的整个项目》，这一篇用对比视角把 Pydantic、dataclass、TypedDict 的定位、取舍和组合方式讲清楚：&lt;strong&gt;谁用于 API 校验、谁承载业务状态、谁只做类型提示&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="目标读者"&gt;目标读者&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;FastAPI / Pydantic 用户，想搞清楚“数据类”该放在哪一层&lt;/li&gt;
&lt;li&gt;有 0–5 年经验、在做服务端建模的 Python 工程师&lt;/li&gt;
&lt;li&gt;已读过前一篇分层文章，想进一步对比具体工具&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="背景--动机为什么要区分三者"&gt;背景 / 动机：为什么要区分三者？&lt;/h3&gt;
&lt;p&gt;在上一篇里，我们强调“Pydantic 应该停留在 API/外围”。很多同学随后会问：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“那 Python 原生 dataclass 呢？和 Pydantic 有什么差？”&lt;/li&gt;
&lt;li&gt;“TypedDict 是不是又一个‘数据类’，要不要取代 dataclass？”&lt;/li&gt;
&lt;li&gt;“什么时候该用 Pydantic dataclasses，什么时候用标准库？”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不区分清楚，常见后果有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用 TypedDict 写业务逻辑，测试时才发现它根本不做运行时校验；&lt;/li&gt;
&lt;li&gt;用 Pydantic BaseModel 传来传去，导致 Domain 强绑定外部依赖；&lt;/li&gt;
&lt;li&gt;dataclass 和 Pydantic 混用，序列化和校验边界越来越模糊。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="核心概念一句话定位"&gt;核心概念：一句话定位&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pydantic BaseModel&lt;/strong&gt;：运行时校验 + 类型转换 + JSON 友好；属于“对外/边界”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dataclass（标准库）&lt;/strong&gt;：轻量数据载体，可承载业务方法；不做自动校验，属于“领域/内部”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TypedDict&lt;/strong&gt;：仅提供静态类型提示，运行时就是普通 dict；属于“静态约束/外部协议”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;主要差异：&lt;/p&gt;</description></item><item><title>排序专题（终篇）：选型实战——按规模、稳定性、内存与分布选择排序算法</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/9.sorting-series-selection-guide/</link><pubDate>Tue, 09 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/9.sorting-series-selection-guide/</guid><description>以实战视角整理排序选型：给出规模/分布/稳定性/内存维度的决策表、工程场景示例、测试清单与常见坑，快速落地前七篇内容。</description></item><item><title>Emmet-Vim 极速指南：用缩写爆写 HTML/CSS</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/frontend/emmet-vim-guide/</link><pubDate>Mon, 08 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/frontend/emmet-vim-guide/</guid><description>给 Vim/Neovim 用户的 Emmet 实战笔记：安装、常用映射、可运行示例、验证清单与常见坑，帮助你在写页面/组件时提升 3 倍速度。</description></item><item><title>别被 AI 牵着走：保持可独立完成的工程能力</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/ai-usage-self-control/</link><pubDate>Mon, 08 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/ai-usage-self-control/</guid><description>讨论在使用 AI 辅助编码时如何避免复制粘贴依赖，结合费曼技巧、刻意练习与检索练习，给出可操作的自检清单与演练步骤。</description></item><item><title>排序专题（八）：TimSort 与 Introsort——语言内置排序的工程范式</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/8.sorting-series-timsort-introsort/</link><pubDate>Mon, 08 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/8.sorting-series-timsort-introsort/</guid><description>拆解 Python/Java 默认的 TimSort 与 C++ std::sort 的 Introsort：触发条件、稳定性、复杂度与工程取舍，附伪实现骨架与选型建议。</description></item><item><title>Svelte 按钮配置全攻略：状态、样式与无障碍实践</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/frontend/svelte-button-config-guide/</link><pubDate>Sun, 07 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/frontend/svelte-button-config-guide/</guid><description>教你在 Svelte 中构建可复用的按钮：动态类名、可选链/空值合并、安全取值、状态样式映射、无障碍支持、测试与常见陷阱。</description></item><item><title>排序专题（七）：非比较排序——计数、桶、基数的范围与位数之战</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/7.sorting-series-non-comparison/</link><pubDate>Sun, 07 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/7.sorting-series-non-comparison/</guid><description>讲清非比较排序的适用前提、时间/空间复杂度、工程实现细节与常见坑，附计数/桶/基数排序的多语言示例。</description></item><item><title>排序专题（六）：堆排序——原地 O(n log n) 的稳健方案</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/6.sorting-series-heap-sort/</link><pubDate>Sat, 06 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/6.sorting-series-heap-sort/</guid><description>讲解堆排序的原理、复杂度与工程场景，对比快排/归并的取舍，附多语言实现和 top-k 应用示例。</description></item><item><title>排序专题（五）：快速排序——枢轴策略、尾递归优化与工程实战</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/5.sorting-series-quick-sort/</link><pubDate>Fri, 05 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/5.sorting-series-quick-sort/</guid><description>全面讲解快速排序的核心思想、枢轴选择、重复元素分区、尾递归与混合排序实践，附多语言实现与工程选型建议。</description></item><item><title>最大正负数计数：用二分在排序数组中统计正整数和负整数数量的最大值（LeetCode 2529）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/2529-maximum-count-of-positive-integer-and-negative-integer/</link><pubDate>Thu, 04 Dec 2025 11:40:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/2529-maximum-count-of-positive-integer-and-negative-integer/</guid><description>在排序数组中统计正整数和负整数数量的最大值（Maximum Count of Positive &amp;amp; Negative Integers）。本文用上下界二分模板一次性找到负数结束位置和正数起始位置，并给出多语言实现与工程应用示例。</description></item><item><title>比目标字母大的最小字母：有序字符数组上的二分查找技巧（LeetCode 744）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/744-find-smallest-letter-greater-than-target/</link><pubDate>Thu, 04 Dec 2025 11:30:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/744-find-smallest-letter-greater-than-target/</guid><description>在排序字符数组中查找比目标字母大的最小字母（Find Smallest Letter Greater Than Target），支持环绕。本文用 upper_bound 二分模板稳定处理边界，并提供多语言实现和工程实践示例。</description></item><item><title>经典 Binary Search：在排序数组中查找目标值索引的统一模板（LeetCode 704）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/704-binary-search/</link><pubDate>Thu, 04 Dec 2025 11:20:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/binary-search/704-binary-search/</guid><description>在有序数组中查找目标值：存在返回下标，不存在返回 -1。本文以 LeetCode 704 为例，用统一的二分模板讲清楚边界处理，并给出多语言实现和工程实践示例。</description></item><item><title>Hot100：Search Insert Position 排序数组中目标值插入位置的二分查找实战（LeetCode 35）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/35-search-insert-position/</link><pubDate>Thu, 04 Dec 2025 11:10:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/35-search-insert-position/</guid><description>在有序数组中寻找目标值插入位置，使数组仍然有序：存在返回下标，不存在返回应插入的位置。本文用统一的 lower_bound 二分模板实现 Search Insert Position，并给出多语言代码与工程应用示例。</description></item><item><title>LeetCode 34：在排序数组中查找元素的第一个和最后一个位置</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/34-find-first-and-last-position-of-element-in-sorted-array/</link><pubDate>Thu, 04 Dec 2025 11:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/hot100/34-find-first-and-last-position-of-element-in-sorted-array/</guid><description>从线性扫描基线出发，推导两个半开区间二分边界，在 O(log n) 时间内返回目标值的完整范围。</description></item><item><title>咒语与药水的成功组合：排序 + 二分查找秒杀乘积约束问题（LeetCode 2300）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/2300-successful-pairs-of-spells-and-potions/</link><pubDate>Thu, 04 Dec 2025 10:50:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/2300-successful-pairs-of-spells-and-potions/</guid><description>给定 spells 和 potions 两个数组以及 success 阈值，如何用排序 + 二分查找在 O((n+m)log m) 时间内求出每个咒语能与多少药水形成乘积 ≥ success 的成功组合，并给出多语言实现与工程应用示例。</description></item><item><title>固定长度子数组 + 至少 m 个不同元素：几乎唯一子数组的最大和（LeetCode 2841）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/2841-maximum-sum-of-almost-unique-subarray/</link><pubDate>Thu, 04 Dec 2025 10:40:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/2841-maximum-sum-of-almost-unique-subarray/</guid><description>给定整数数组 nums 和正整数 m、k，要求在所有长度为 k、且至少包含 m 个不同元素的子数组中，找到最大和；若不存在返回 0。本文用滑动窗口 + 计数哈希表推导 O(n) 解法，并提供多语言实现与工程应用示例。</description></item><item><title>最少涂色次数拿到 k 个连续黑块：滑动窗口的极简解法（LeetCode 2379）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/2379-minimum-recolors-to-get-k-consecutive-black-blocks/</link><pubDate>Thu, 04 Dec 2025 10:30:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/2379-minimum-recolors-to-get-k-consecutive-black-blocks/</guid><description>给定只包含 W/B 的字符串 blocks，每次可将一个 W 涂成 B，如何用一次线性扫描求出得到至少一次连续 k 个黑色块的最少操作次数，并给出多语言实现与工程应用示例。</description></item><item><title>排序专题（四）：归并排序——稳定分治与外部排序的首选</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/4.sorting-series-merge-sort/</link><pubDate>Thu, 04 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/4.sorting-series-merge-sort/</guid><description>系统讲解归并排序的分治原理、稳定性、空间取舍与工程场景，附 Python/C/C++/Go/Rust/JS 实现、外部排序思路与选型建议。</description></item><item><title>排序专题（三）：希尔排序——从插入到分组增量的效率跃迁</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/3.sorting-series-shell-sort/</link><pubDate>Wed, 03 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/3.sorting-series-shell-sort/</guid><description>深入解析希尔排序的原理、增量策略与工程用法，附多场景示例和 Python/C/C++/Go/Rust/JS 实现，帮助理解从插入到 O(n log^2 n) 的过渡。</description></item><item><title>排序专题（二）：冒泡、选择、插入——三种 O(n^2) 基线的对比与取舍</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/2.sorting-series-on2-baseline/</link><pubDate>Tue, 02 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/2.sorting-series-on2-baseline/</guid><description>用 ACERS 模板系统讲解冒泡/选择/插入排序的原理、稳定性、适用场景与工程示例，并给出多语言实现与选型建议。</description></item><item><title>以业务对象为核心的 Python 架构实践</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-business-object-first-architecture/</link><pubDate>Mon, 01 Dec 2025 11:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/python-business-object-first-architecture/</guid><description>通过一个工单（Ticket）示例，讲解在 Python + FastAPI + SQLAlchemy 项目中，如何先定义清晰的业务对象（领域模型），再围绕它设计接口 DTO、仓储抽象和应用服务，避免业务代码直接围绕表结构和框架 API 打转。</description></item><item><title>从表结构到领域模型：用聚合仓储设计权限系统</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/permission-architecture-aggregate-repository/</link><pubDate>Mon, 01 Dec 2025 10:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/permission-architecture-aggregate-repository/</guid><description>本文通过 FastAPI + SQLAlchemy + Alembic 的权限组管理示例，讲解如何用领域模型和仓储抽象设计权限系统，为什么仓储可以一次操作多张表，以及 service 层和 repository 层各自应该承担的职责。</description></item><item><title>排序专题序章：如何选算法——时间/空间/稳定性/场景速查</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/1.sorting-series-preface/</link><pubDate>Mon, 01 Dec 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/sorting/1.sorting-series-preface/</guid><description>用 ACERS 模板快速梳理常见排序算法的适用场景、复杂度、稳定性与工程实现，附多语言可运行示例与选型清单。</description></item><item><title>Alembic 入门：第一次用 SQLAlchemy 做数据库迁移</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/alembic-intro-sqlalchemy-migrations/</link><pubDate>Fri, 28 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/alembic-intro-sqlalchemy-migrations/</guid><description>&lt;h1 id="-alembic-入门第一次用-sqlalchemy-做数据库迁移"&gt;🐣 Alembic 入门：第一次用 SQLAlchemy 做数据库迁移&lt;/h1&gt;
&lt;h2 id="-副标题--摘要"&gt;💡 副标题 / 摘要&lt;/h2&gt;
&lt;p&gt;如果你已经在用 SQLAlchemy 操作数据库，却还在靠“手工改表结构 + 导出导入 SQL”来维护 schema，这篇文章会带你用最小成本上手 Alembic。&lt;br&gt;
我们会从 &lt;strong&gt;0 配置 Alembic&lt;/strong&gt; 开始，一步步完成：生成迁移、升级/回滚数据库、和 SQLAlchemy 模型联动。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;🎯 目标读者&lt;/h2&gt;
&lt;p&gt;适合这样的你：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;已经在项目中使用 &lt;strong&gt;SQLAlchemy&lt;/strong&gt;（ORM 或 Core 都行）；&lt;/li&gt;
&lt;li&gt;从未使用过 Alembic，或只懂 &lt;code&gt;alembic upgrade head&lt;/code&gt; 这几个命令；&lt;/li&gt;
&lt;li&gt;想为自己的项目加上 &lt;strong&gt;可回滚、可追踪、可审计&lt;/strong&gt; 的数据库结构变更；&lt;/li&gt;
&lt;li&gt;以 Python / Web 后端为主（Flask / FastAPI / 自研框架均可）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机为什么需要数据库迁移工具"&gt;🔥 背景 / 动机：为什么需要数据库迁移工具？&lt;/h2&gt;
&lt;p&gt;没有 Alembic 时，我们通常怎么改数据库结构？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在本地手改表结构（改字段、加索引）；&lt;/li&gt;
&lt;li&gt;导出 SQL 发给同事 / DBA；&lt;/li&gt;
&lt;li&gt;生产环境再手工执行一次；&lt;/li&gt;
&lt;li&gt;一旦出错，回滚非常痛苦。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常见痛点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多人协作困难&lt;/strong&gt;：谁先改？谁后改？改了什么？&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;环境不一致&lt;/strong&gt;：本地、测试、生产的表结构经常不一样；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;难以回滚&lt;/strong&gt;：一旦上线发现问题，很难安全退回之前版本；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;审计困难&lt;/strong&gt;：几年后根本不知道这个表为什么多了几个字段。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Alembic 做的事情可以总结为一句话：&lt;/p&gt;</description></item><item><title>如何干预 Alembic：从自动生成到精细控制</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/alembic-autogenerate-to-manual-control/</link><pubDate>Fri, 28 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/alembic-autogenerate-to-manual-control/</guid><description>&lt;h1 id="-如何干预-alembic从自动生成到精细控制"&gt;🧬 如何干预 Alembic：从自动生成到精细控制&lt;/h1&gt;
&lt;h2 id="-副标题--摘要"&gt;💡 副标题 / 摘要&lt;/h2&gt;
&lt;p&gt;大多数人用 Alembic 的方式是：改 SQLAlchemy 模型 → &lt;code&gt;alembic revision --autogenerate&lt;/code&gt; → &lt;code&gt;alembic upgrade head&lt;/code&gt;。&lt;br&gt;
但在真实项目里，你往往需要“插手”这条流水线：控制生成的迁移内容、插入数据迁移、在生产环境加保护、按分支管理多套 Schema……&lt;/p&gt;
&lt;p&gt;这篇文章会带你系统认识 &lt;strong&gt;“如何干预 Alembic”&lt;/strong&gt;：&lt;br&gt;
从 &lt;code&gt;env.py&lt;/code&gt; 到单个迁移脚本，从自动生成到手写数据迁移，让你能放心地在生产库上使用 Alembic，而不是被它“牵着走”。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;🎯 目标读者&lt;/h2&gt;
&lt;p&gt;适合以下读者：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;已在项目中使用 &lt;strong&gt;SQLAlchemy + Alembic&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;希望从“只会用 autogenerate”进阶到“懂得控制 Alembic 行为”；&lt;/li&gt;
&lt;li&gt;有生产库 / 多环境（dev、staging、prod）场景，需要更安全的迁移控制；&lt;/li&gt;
&lt;li&gt;想把 &lt;strong&gt;数据迁移&lt;/strong&gt;、&lt;strong&gt;自定义检查&lt;/strong&gt;、&lt;strong&gt;安全保护&lt;/strong&gt; 加进 Alembic 流程的后端工程师。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机为什么要干预-alembic"&gt;🔥 背景 / 动机：为什么要“干预” Alembic？&lt;/h2&gt;
&lt;p&gt;只使用 Alembic 的默认玩法，很容易遇到这些问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--autogenerate&lt;/code&gt; 生成了一堆你不理解的操作，不敢在生产上跑；&lt;/li&gt;
&lt;li&gt;模型删了字段，自动生成的迁移脚本也直接删列，但生产上其实还有老数据需要兜底；&lt;/li&gt;
&lt;li&gt;想在迁移时顺便初始化一些字典表、配置表，但不知放在哪；&lt;/li&gt;
&lt;li&gt;有些表只在测试 / demo 环境需要，生产环境不想创建；&lt;/li&gt;
&lt;li&gt;多个服务共享一个数据库，需要 &lt;strong&gt;按分支/模块控制迁移范围&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要解决这些问题，你就必须学会：&lt;br&gt;
&lt;strong&gt;在 Alembic 的各个“接缝处”插入自己的逻辑&lt;/strong&gt;。&lt;/p&gt;</description></item><item><title>用 UFW + CrowdSec，彻底阻止恶意端口扫描：从 Fail2ban 踩坑到终极解决方案</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/ufw-crowdsec-portscan/</link><pubDate>Sat, 22 Nov 2025 12:00:00 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/ufw-crowdsec-portscan/</guid><description>&lt;h1 id="-用-ufw--crowdsec彻底阻止恶意端口扫描"&gt;🛡️ 用 UFW + CrowdSec，彻底阻止恶意端口扫描&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要：&lt;/strong&gt; 如何安全防护你的服务器暴露端口？本文带你从 Fail2ban 的正则地狱走出，构建一个稳定、自动化、智能化的端口扫描防御系统。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;🎯 目标读者&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;使用 FRP / 内网穿透的开发者&lt;/li&gt;
&lt;li&gt;管理云服务器（腾讯云、阿里云、AWS 等）的运维人员&lt;/li&gt;
&lt;li&gt;想防御端口扫描、SSH 暴力破解的新手或中级 Linux 用户&lt;/li&gt;
&lt;li&gt;对 Fail2ban 感兴趣、想升级到更现代安全体系的人&lt;/li&gt;
&lt;li&gt;想完善服务器安全方案的个人开发者&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机为什么需要端口扫描防护"&gt;💢 背景 / 动机：为什么需要端口扫描防护？&lt;/h2&gt;
&lt;p&gt;在运行 FRP（frps + frpc）或开放多个端口时，你的服务器通常会遭遇：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;海量扫描：每秒多次 SYN 探测&lt;/li&gt;
&lt;li&gt;恶意连接尝试：get a user connection [&amp;hellip;]&lt;/li&gt;
&lt;li&gt;SSH 密码爆破&lt;/li&gt;
&lt;li&gt;自动化脚本扫描 6001–6010、7000、22、8080 等常见端口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;传统做法存在痛点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;防火墙（UFW）只能被动拒绝&lt;/li&gt;
&lt;li&gt;Fail2ban 配置复杂、依赖正则、容易误判、不支持高级行为分析&lt;/li&gt;
&lt;li&gt;FRPS 日志格式特殊，Fail2ban 很难匹配&lt;/li&gt;
&lt;li&gt;攻击会占用 frps/sshd 资源，最终导致卡顿、断流&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，我们需要一个&lt;strong&gt;无需写正则、能自动检测扫描、智能封禁恶意 IP 的现代防御体系&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;📘 核心概念&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FRP（frps / frpc）&lt;/strong&gt;：用于内网穿透，常暴露大量 TCP 端口（如 6001–6010），容易被扫描。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UFW（Uncomplicated Firewall）&lt;/strong&gt;：Ubuntu 默认防火墙，但缺乏智能检测功能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fail2ban&lt;/strong&gt;：传统日志匹配型封禁工具，需要手写正则，踩坑概率高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CrowdSec（推荐）&lt;/strong&gt;：新一代开放式入侵防御系统 (IPS)，自动检测端口扫描和暴力破解，事件驱动 + 行为分析，资源消耗极低，是 Fail2ban 的现代替代。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南使用-crowdsec-自动阻止端口扫描ubuntudebian"&gt;🛠 实践指南：使用 CrowdSec 自动阻止端口扫描（Ubuntu/Debian）&lt;/h2&gt;
&lt;h3 id="1-安装-crowdsec"&gt;1) 安装 CrowdSec&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt install crowdsec -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-安装防火墙封禁组件iptables--ufw-自动配合"&gt;2) 安装防火墙封禁组件（iptables / ufw 自动配合）&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt install crowdsec-firewall-bouncer-iptables
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CrowdSec 会自动接管封禁动作。&lt;/p&gt;</description></item><item><title>🛡️ WireGuard 全面指南：构建安全高速的私人内网（VPN 实战教程）</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/wireguard-vpn-neiwang/</link><pubDate>Thu, 20 Nov 2025 07:55:02 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/wireguard-vpn-neiwang/</guid><description>&lt;h1 id="-wireguard-全面指南构建安全高速的私人内网vpn-实战教程"&gt;🛡️ WireGuard 全面指南：构建安全高速的私人内网（VPN 实战教程）&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要：&lt;/strong&gt;
本文是一篇适合初学者与中级用户的 WireGuard VPN 入门与实战指南。你将学会如何搭建高速、安全、现代化的内网，并实现“服务不暴露公网，只能通过 VPN 访问”的零信任式安全架构。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;👤 目标读者&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;想用 VPN 隐藏自己服务器/电脑端口的人&lt;/li&gt;
&lt;li&gt;想提高服务器安全性、避免被扫描的人&lt;/li&gt;
&lt;li&gt;希望构建私人内网 / 远程访问家庭电脑的人&lt;/li&gt;
&lt;li&gt;Linux / Windows / 开发者 / 运维初学者&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景与动机为什么你需要-wireguard"&gt;🎯 背景与动机：为什么你需要 WireGuard？&lt;/h2&gt;
&lt;p&gt;现代互联网环境下，一旦你的服务器开放端口到公网（SSH、数据库、后台服务），就会：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;持续被扫描&lt;/li&gt;
&lt;li&gt;遭遇密码爆破&lt;/li&gt;
&lt;li&gt;被爬虫探测漏洞&lt;/li&gt;
&lt;li&gt;面临潜在入侵风险&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;传统解决方案如 OpenVPN 虽然成熟，但复杂、速度慢、配置烦琐。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WireGuard 是为现代安全而生的 VPN：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;小巧、安全、快，如同“下一代 VPN 协议”&lt;/li&gt;
&lt;li&gt;代码量 &amp;lt; 4000 行（OpenVPN 是 40 万+）&lt;/li&gt;
&lt;li&gt;极易配置&lt;/li&gt;
&lt;li&gt;延迟低、带宽高&lt;/li&gt;
&lt;li&gt;适合自建内网、服务器保护、远程办公&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文将教你如何用 WireGuard 构建一个&lt;strong&gt;完全隐藏在互联网上的私人内网&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="-核心概念"&gt;🔑 核心概念&lt;/h1&gt;
&lt;h2 id="wireguard-是什么"&gt;&lt;strong&gt;WireGuard 是什么？&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;WireGuard 是一种现代化、极简、安全的 VPN 协议，运行在 Linux 内核中，使用最先进的加密算法（ChaCha20、Curve25519 等）。&lt;/p&gt;</description></item><item><title>让 FastAPI 异步真正‘不卡’：asyncio.create_task + to_thread 并发实践（含 MySQL 写入）</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/fastapi-asyncio-create-task-to-thread-mysql/</link><pubDate>Wed, 19 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/fastapi-asyncio-create-task-to-thread-mysql/</guid><description>把同步重活丢给线程、把可并行的子流程拆出来并发执行，让 FastAPI WebSocket/HTTP 服务在高并发文件处理场景下保持流畅与可靠。</description></item><item><title>现代加密替代方案：AES‑GCM 与 ChaCha20‑Poly1305 实战指南（附 Python 示例）</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/modern-crypto-aes-gcm-chacha20-poly1305-guide/</link><pubDate>Wed, 19 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/modern-crypto-aes-gcm-chacha20-poly1305-guide/</guid><description>聚焦现代 AEAD：为何替代 RC4、如何安全落地 AES‑GCM 与 ChaCha20‑Poly1305，附可复制的 Python 代码与最佳实践。</description></item><item><title>用 Python 还原 RC4 + JWT + 自定义 SSO Token 加解密（含可运行示例）</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/recreate-rc4-jwt-custom-sso-token-in-python/</link><pubDate>Wed, 19 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/recreate-rc4-jwt-custom-sso-token-in-python/</guid><description>从核心概念到可运行代码，演示用 Python 实现 RC4 加/解密、JWT 与自定义 SSO Token，并讨论风险与替代方案。示例中密钥与发行方均为占位值。</description></item><item><title>一位与两位编码解析的刷题笔记与工程应用全解析（续集，LeetCode 717）</title><link>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/717-1-bit-and-2-bit-characters/</link><pubDate>Tue, 18 Nov 2025 23:43:17 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/alg/leetcode/717-1-bit-and-2-bit-characters/</guid><description>&lt;h1 id="一位与两位编码解析的刷题笔记与工程应用全解析续集leetcode-717"&gt;&lt;strong&gt;一位与两位编码解析的刷题笔记与工程应用全解析（续集，LeetCode 717）&lt;/strong&gt;&lt;/h1&gt;
&lt;h2 id="副标题--摘要"&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;本文解析 LeetCode《1-bit and 2-bit Characters》题目，讲解如何用简单的指针跳跃算法解析二进制编码序列，并展示该算法在通信协议、数据格式解析、事件流处理等工程场景中的真实应用。适合希望将算法题知识迁移到工程系统的开发者。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="目标读者"&gt;&lt;strong&gt;目标读者&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;刷 LeetCode、准备技术面试的开发者&lt;/li&gt;
&lt;li&gt;对通信协议、序列解析、数据流处理感兴趣的工程师&lt;/li&gt;
&lt;li&gt;想提升“抽象能力与工程迁移能力”的同学&lt;/li&gt;
&lt;li&gt;从事监控、序列分析、协议解析等工作的后端开发者&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="背景--动机为什么这题值得写一篇博客"&gt;&lt;strong&gt;背景 / 动机：为什么这题值得写一篇博客？&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;乍一看，这道题好像只是简单判断：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个由 0/1 组成的编码流，最后一个 0 是否单独构成一个 1 位字符？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但本质上它对应的是 &lt;strong&gt;“变长编码（Variable-Length Coding）解析”&lt;/strong&gt;，而变长编码在工程中极其常见，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UTF-8 字符解析&lt;/li&gt;
&lt;li&gt;网络包头编码解析&lt;/li&gt;
&lt;li&gt;字节码指令流解析&lt;/li&gt;
&lt;li&gt;数据压缩（如 Huffman Coding）&lt;/li&gt;
&lt;li&gt;通信协议中的 Frame 解析&lt;/li&gt;
&lt;li&gt;行为序列中用跳表式结构编码的事件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，这道题不仅是算法题，更是“从左向右解析变长编码的模型题”。&lt;/p&gt;
&lt;p&gt;理解这题，就是理解大量系统底层的基础。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="核心概念"&gt;&lt;strong&gt;核心概念&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id="1-变长编码variable-length-encoding"&gt;&lt;strong&gt;1. 变长编码（Variable-Length Encoding）&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;题目中规定了两种编码：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1 位字符&lt;/strong&gt;：&lt;code&gt;0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2 位字符&lt;/strong&gt;：&lt;code&gt;10&lt;/code&gt; 或 &lt;code&gt;11&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是一种简化的变长编码结构：字符长度取决于首位。&lt;/p&gt;
&lt;p&gt;工程中常见：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;系统&lt;/th&gt;
&lt;th&gt;变长规则&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UTF-8&lt;/td&gt;
&lt;td&gt;1~4 字节，根据前缀位判断长度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TLV 协议&lt;/td&gt;
&lt;td&gt;T + 长度字段决定 Value 长度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;字节码流&lt;/td&gt;
&lt;td&gt;opcode 决定后续参数个数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;硬件指令集&lt;/td&gt;
&lt;td&gt;有变长和固定长度两类&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;题目正是这些系统的「极简模型」。&lt;/p&gt;</description></item><item><title>How to Build a Blog System</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/how-to-build-a-blog-system/</link><pubDate>Fri, 14 Nov 2025 15:04:02 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/how-to-build-a-blog-system/</guid><description>&lt;h1 id="标题用-hugo--github-pages-十分钟上线个人博客超详细新手指南"&gt;&lt;strong&gt;标题：用 Hugo + GitHub Pages 十分钟上线个人博客（超详细新手指南）&lt;/strong&gt;&lt;/h1&gt;
&lt;h2 id="副标题--摘要"&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;本教程带你从零开始，将本地 Hugo 博客部署到 GitHub Pages，全程只需 10 分钟，适合想快速上线技术博客、文档站点的开发者。确保你不仅能跑起来，还能理解背后的工作原理。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="目标读者"&gt;&lt;strong&gt;目标读者&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Hugo 初学者&lt;/li&gt;
&lt;li&gt;想快速上线个人技术博客的开发者&lt;/li&gt;
&lt;li&gt;想了解 GitHub Pages + GitHub Actions 部署的用户&lt;/li&gt;
&lt;li&gt;想要零成本托管静态网站的同学&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="背景--动机为什么要用-hugo--github-pages"&gt;&lt;strong&gt;背景 / 动机：为什么要用 Hugo + GitHub Pages？&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;许多人写博客时面临这些痛点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;发布文章要手动上传，不自动化&lt;/li&gt;
&lt;li&gt;静态站点生成器很多，但部署步骤零散&lt;/li&gt;
&lt;li&gt;GitHub Pages 文档不够清晰，新手容易踩坑&lt;/li&gt;
&lt;li&gt;主题（如 PaperMod）需要正确处理资源（SCSS）才能编译成功&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Hugo + GitHub Pages + GitHub Actions 组合&lt;/strong&gt; 完美解决了这些问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hugo 构建速度极快（上千文章依旧瞬间生成）&lt;/li&gt;
&lt;li&gt;GitHub Pages 完全免费，不需要服务器&lt;/li&gt;
&lt;li&gt;GitHub Actions 自动部署，写完文章 push 即上线&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="核心概念必须理解"&gt;&lt;strong&gt;核心概念（必须理解）&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id="1-hugo"&gt;&lt;strong&gt;1. Hugo&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;一个超快的静态博客生成器，通过 Markdown 生成 HTML。&lt;/p&gt;
&lt;h3 id="2-github-pages"&gt;&lt;strong&gt;2. GitHub Pages&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;GitHub 提供的免费静态网站托管。&lt;/p&gt;</description></item><item><title>How to Publish by Hugo</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/how-to-publish-by-hugo/</link><pubDate>Fri, 14 Nov 2025 15:01:32 +0800</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/how-to-publish-by-hugo/</guid><description>&lt;h1 id="标题如何使用-hugo-发布文章从-markdown-到线上博客的全流程指南"&gt;&lt;strong&gt;标题：如何使用 Hugo 发布文章：从 Markdown 到线上博客的全流程指南&lt;/strong&gt;&lt;/h1&gt;
&lt;h2 id="副标题--摘要"&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;这篇文章教你如何使用 Hugo 创建、管理与发布文章，包括 front matter 设置、草稿管理、图片处理、目录结构、预览与上线，让你从零掌握完整写作流程。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="目标读者"&gt;&lt;strong&gt;目标读者&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Hugo 初学者&lt;/li&gt;
&lt;li&gt;想用 Hugo 搭建技术博客的人&lt;/li&gt;
&lt;li&gt;想学习 Markdown + 静态站点写作流程的开发者&lt;/li&gt;
&lt;li&gt;使用 PaperMod、DoIt 等主题的用户&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="背景--动机"&gt;&lt;strong&gt;背景 / 动机&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;很多人在成功搭建 Hugo 博客后会遇到新的困惑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文章应该放在哪个目录？&lt;/li&gt;
&lt;li&gt;front matter 要怎么写？&lt;/li&gt;
&lt;li&gt;图片要放哪？&lt;/li&gt;
&lt;li&gt;为什么本地能看到文章但线上看不到？&lt;/li&gt;
&lt;li&gt;草稿 / 发布时间如何控制？&lt;/li&gt;
&lt;li&gt;怎样让文章自动出现在首页？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些都是 Hugo 新手非常常见的痛点。
本教程用&lt;strong&gt;实战步骤 + 最佳实践&lt;/strong&gt;帮助你完全掌握“如何发布文章”的整个流程。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="核心概念"&gt;&lt;strong&gt;核心概念&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id="1-hugo-content内容目录"&gt;&lt;strong&gt;1. Hugo Content（内容目录）&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Hugo 的文章都放在 &lt;code&gt;content/&lt;/code&gt; 目录下，比如：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;content/
posts/
my-first-post.md
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2-front-matter"&gt;&lt;strong&gt;2. Front Matter&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;文章头部的三段 YAML/TOML/JSON，用来控制文章：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;文章标题&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;date&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;2024-08-26&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;draft&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;tags&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;hugo&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;blog&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-draft草稿"&gt;&lt;strong&gt;3. Draft（草稿）&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;草稿不会被构建，只能在本地用 &lt;code&gt;hugo server -D&lt;/code&gt; 查看。&lt;/p&gt;</description></item><item><title>用一段优雅的python代码，把sqlalchemy模型高效转为字典</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/sqlalchemy-model-to-dict-python/</link><pubDate>Wed, 12 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/sqlalchemy-model-to-dict-python/</guid><description>&lt;p&gt;&lt;strong&gt;标题&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用一段优雅的 Python 代码，把 SQLAlchemy 模型安全、高效地序列化成字典&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SQLAlchemy 模型转字典（dict）看似简单，却暗藏字段格式、关系递归、循环引用等坑。本文通过一段实战代码，带你实现一个可复用的 &lt;code&gt;_to_dict&lt;/code&gt; 序列化工具，并分析其设计取舍与改进方向，适合正在用 SQLAlchemy 写后端接口的你。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;目标读者&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这篇文章适合以下读者：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;strong&gt;SQLAlchemy&lt;/strong&gt; 做 ORM 的后端开发者&lt;/li&gt;
&lt;li&gt;想把 &lt;strong&gt;ORM 模型转换为 JSON/dict&lt;/strong&gt; 的 Python 工程师&lt;/li&gt;
&lt;li&gt;对 &lt;strong&gt;模型序列化规范化&lt;/strong&gt; 有需求的中级开发者&lt;/li&gt;
&lt;li&gt;使用 Flask/FastAPI/Django + SQLAlchemy 的同学&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="一背景--动机为什么要自己写-_to_dict"&gt;一、背景 / 动机：为什么要自己写 &lt;code&gt;_to_dict&lt;/code&gt;？&lt;/h2&gt;
&lt;p&gt;在 Web 开发中，我们几乎每天都要做一件事：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;把数据库里的 ORM 对象，转成可以 JSON 响应给前端的数据结构（通常是 dict / list）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;乍一看好像只是 &lt;code&gt;obj.__dict__&lt;/code&gt; 或用个 &lt;code&gt;asdict&lt;/code&gt; 就完事，但现实中的问题包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;日期时间字段无法直接 JSON 化&lt;/strong&gt;：
&lt;code&gt;datetime&lt;/code&gt; / &lt;code&gt;date&lt;/code&gt; 对象不能直接 JSON 序列化，必须格式化成字符串。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;关系字段怎么处理？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一对多 / 多对多（&lt;code&gt;uselist=True&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;一对一 / 多对一（&lt;code&gt;uselist=False&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;避免递归爆炸&lt;/strong&gt;：
两个模型互相关联，很容易序列化时陷入无限递归。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;统一输出格式&lt;/strong&gt;：
不同模型、不同接口如果各写各的 &lt;code&gt;to_dict&lt;/code&gt;，维护成本极高。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>用Issue把问题写清楚，从0到TEMPLATE</title><link>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/write-clear-issues-from-zero-to-template/</link><pubDate>Tue, 11 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/write-clear-issues-from-zero-to-template/</guid><description>&lt;h2 id="标题吸引且准确包含关键词"&gt;标题（吸引且准确，包含关键词）&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;用 Issue 模板把需求写清楚：从 0 配置 GitHub Issue Template 的完整指南&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="副标题--摘要"&gt;副标题 / 摘要&lt;/h2&gt;
&lt;p&gt;这篇文章手把手教你在 GitHub 仓库中配置「新需求 / Feature」与「Bug」Issue 模板，包括目录结构、YAML 表单、Markdown 模板以及常见坑。适合想让团队需求沟通更规范、减少反复追问的开发者和团队负责人。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="目标读者"&gt;目标读者&lt;/h2&gt;
&lt;p&gt;这篇文章适合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;经常在 GitHub 仓库里开 Issue、提需求的 &lt;strong&gt;后端 / 前端 / 全栈工程师&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;想把团队需求提交流程「标准化」的 &lt;strong&gt;项目负责人 / TL / 架构师&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;对 GitHub 已经有基本使用经验、但还没用过 Issue 模板的 &lt;strong&gt;中级开发者&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;完全新手也能看懂，但会默认你知道：什么是仓库、什么是 Issue、如何提交代码等。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="背景--动机为什么要折腾-issue-模板"&gt;背景 / 动机：为什么要折腾 Issue 模板？&lt;/h2&gt;
&lt;p&gt;没有 Issue 模板时，日常可能是这样的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“这个需求背景是什么？”&lt;/li&gt;
&lt;li&gt;“影响哪些模块？”&lt;/li&gt;
&lt;li&gt;“验收标准怎么算通过？”&lt;/li&gt;
&lt;li&gt;“优先级到底多高？”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话 Issue：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“做个导出功能”
直接把所有人整破防。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;长期下来会有几个痛点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;沟通成本高&lt;/strong&gt;：每个需求都要反复追问细节；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;信息不对称&lt;/strong&gt;：请求人脑子里很清楚，但写在 Issue 里的只有一句话；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;难以排期&lt;/strong&gt;：没有明确优先级和验收标准，大家都觉得自己的需求是 P0；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;历史难追踪&lt;/strong&gt;：几个月后再看这个 Issue，完全不知道当时怎么想的。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而 GitHub 提供的 &lt;strong&gt;Issue Template&lt;/strong&gt;，其实就是一套「结构化提问」工具：&lt;/p&gt;</description></item><item><title>别让Pydantic占领你的整个项目:聊聊API校验,Domain模型和数据库之间的边界</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pydantic-boundaries-api-domain-db/</link><pubDate>Sun, 09 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pydantic-boundaries-api-domain-db/</guid><description>&lt;h3 id="标题"&gt;标题&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;别让 Pydantic 占领你的整个项目：聊聊 API 校验、Domain 模型和数据库之间的边界&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="副标题--摘要"&gt;副标题 / 摘要&lt;/h3&gt;
&lt;p&gt;很多用 FastAPI/Pydantic 的 Python 工程师，会不知不觉让 Pydantic Model 贯穿 API、业务、数据库所有层。本文用一个清晰的分层思路和完整代码示例，帮你搞清楚：&lt;strong&gt;Pydantic 适合用在什么地方，Domain / ORM 又应该怎么配合。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="目标读者"&gt;目标读者&lt;/h3&gt;
&lt;p&gt;这篇文章适合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;正在使用 &lt;strong&gt;FastAPI / Pydantic / SQLAlchemy / SQLModel&lt;/strong&gt; 的 Python 后端工程师&lt;/li&gt;
&lt;li&gt;刚入行 0–3 年、开始关心“分层、架构、领域模型”的开发者&lt;/li&gt;
&lt;li&gt;想从“会写接口”进阶到“懂业务建模、懂分层”的工程师&lt;/li&gt;
&lt;li&gt;对 “Pydantic 要不要进 Domain / 要不要用于 DB 模型” 有疑惑的人&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="一背景--动机为什么-pydantic-容易长满全项目"&gt;一、背景 / 动机：为什么 Pydantic 容易“长满全项目”？&lt;/h2&gt;
&lt;p&gt;如果你是从 FastAPI 入门后端，很可能经历过这样的路径：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;用 Pydantic 定义请求体、响应体：太好用了，自动校验 + 文档 + 类型提示。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;觉得既然 Pydantic 这么香，那干脆：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接拿 Pydantic Model 当“业务对象”传来传去&lt;/li&gt;
&lt;li&gt;甚至顺手拿它去做“数据库模型”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;渐渐地，你的项目变成：&lt;/p&gt;</description></item><item><title>从写路由到写"大脑":Python工程师如何先搞定核心逻辑,再考虑API</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/core-logic-before-api-for-python-engineers/</link><pubDate>Sat, 08 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/core-logic-before-api-for-python-engineers/</guid><description>&lt;h1 id="标题"&gt;&lt;strong&gt;标题&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;从写路由到写“大脑”：Python 工程师如何先搞定核心逻辑，再考虑 API&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="副标题--摘要"&gt;副标题 / 摘要&lt;/h2&gt;
&lt;p&gt;刚入行时，我们常常一上来就写路由、设计接口、想 chat_id / message_id 怎么存，却发现真正的“智力活”——核心逻辑——总是拖到后面。这篇文章带你从「先写接口」的思维，升级到「先写大脑，再接外壳」，并串起来六边形架构、Clean Architecture、DDD 等背后的经典理念。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="目标读者"&gt;目标读者&lt;/h2&gt;
&lt;p&gt;适合这些同学阅读：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1–3 年经验&lt;/strong&gt; 的 Python 后端工程师 / AI 应用开发者&lt;/li&gt;
&lt;li&gt;正在用 &lt;strong&gt;FastAPI / Django / Flask&lt;/strong&gt; 等框架写 API 的工程师&lt;/li&gt;
&lt;li&gt;想从“CRUD 搬砖工”进化为“懂设计、能抽象”的工程师&lt;/li&gt;
&lt;li&gt;对 &lt;strong&gt;六边形架构 / Clean Architecture / DDD&lt;/strong&gt; 有点好奇但没系统看过书的人&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1 id="一背景--动机为什么先写接口会卡死自己"&gt;一、背景 / 动机：为什么“先写接口”会卡死自己？&lt;/h1&gt;
&lt;p&gt;很多刚入行的 Python 工程师（包括你我）会有这样的流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;产品提一个新需求：做一个 AI 聊天功能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开编辑器，第一反应就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设计 URL：&lt;code&gt;POST /api/chat/send_message&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;开始写 router：&lt;code&gt;@app.post(&amp;quot;/chat/send&amp;quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;想 request body 参数长什么样：&lt;code&gt;chat_id / message_id / user_id / content&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;想数据库表结构：&lt;code&gt;chats&lt;/code&gt;，&lt;code&gt;messages&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;写了一堆 API、schema、model、迁移脚本之后，才想起来：
&lt;strong&gt;“那 AI 回复到底是怎么生成的？”&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>api标准</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/api-standards/</link><pubDate>Thu, 06 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/api-standards/</guid><description>&lt;h1 id="-标题"&gt;🧭 标题：&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;如何编写一份合格的 API 文档：从 Tony Tam 的 Swagger 到现代 OpenAPI 实践&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-副标题--摘要"&gt;✍️ 副标题 / 摘要&lt;/h2&gt;
&lt;p&gt;想让你的 API 被开发者真正用得舒服？这篇文章将带你从理念到实践，全面掌握一份高质量 API 文档的结构、示例与最佳规范，基于 Tony Tam 提出的 Swagger / OpenAPI 标准。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;🎯 目标读者&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;初学者：想了解 API 文档标准结构的人。&lt;/li&gt;
&lt;li&gt;中级开发者：希望提升接口文档可维护性与规范性的人。&lt;/li&gt;
&lt;li&gt;架构师 / 技术负责人：负责 API 设计规范制定与团队协作的人。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机"&gt;💡 背景 / 动机&lt;/h2&gt;
&lt;p&gt;许多开发团队的 API 文档存在以下痛点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;信息零散，缺乏统一格式；&lt;/li&gt;
&lt;li&gt;更新滞后，开发与文档脱节；&lt;/li&gt;
&lt;li&gt;无法直接用于自动生成或测试。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tony Tam 于 2010 年提出的 &lt;strong&gt;Swagger 规范（后更名为 OpenAPI）&lt;/strong&gt; 正是为了解决这些问题。如今，它已成为 RESTful API 文档的事实标准，被 Google、Amazon、Stripe 等公司广泛采用。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;🔍 核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API 文档&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;描述应用程序接口如何被调用、请求与响应的技术说明书。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Swagger / OpenAPI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一种用于定义、生成、测试 REST API 的标准化规范。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Endpoint（端点）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API 中可访问的具体路径（如 &lt;code&gt;/users/{id}&lt;/code&gt;）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema（数据模型）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;定义请求与响应的字段结构。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南--步骤"&gt;🧰 实践指南 / 步骤&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;明确文档结构&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;概述（Overview）&lt;/li&gt;
&lt;li&gt;鉴权机制（Authentication）&lt;/li&gt;
&lt;li&gt;接口定义（Endpoints）&lt;/li&gt;
&lt;li&gt;数据模型（Schemas）&lt;/li&gt;
&lt;li&gt;错误码与示例（Errors &amp;amp; Examples）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;使用 OpenAPI 规范组织文档&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;建议采用 YAML 格式，支持机器可读与可视化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;推荐工具链&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;编辑器：Swagger Editor、Stoplight Studio、VS Code + YAML 插件&lt;/li&gt;
&lt;li&gt;文档展示：Swagger UI / ReDoc&lt;/li&gt;
&lt;li&gt;自动生成：通过注释生成（如 Springdoc、FastAPI、NestJS）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>从堵塞到异步:为什么上传文件接口不该等文件处理完</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/from-blocking-to-async-file-upload-processing/</link><pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/from-blocking-to-async-file-upload-processing/</guid><description>&lt;h1 id="-从阻塞到异步为什么上传接口不该等文件处理完"&gt;🚀 从阻塞到异步：为什么上传接口不该等文件处理完？&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;—— 用异步任务和状态跟踪构建高性能文件处理系统&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-副标题--摘要"&gt;🧭 副标题 / 摘要&lt;/h2&gt;
&lt;p&gt;在现代 Web 系统中，文件上传只是起点，真正的挑战在于后续的解析、索引和处理。本文带你理解为什么“上传接口不等待处理完成”是现代架构的核心理念，以及如何通过异步任务 + 状态查询实现稳定、可扩展的后台处理系统。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;👥 目标读者&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;有一定 Web 开发经验的工程师（Python/FastAPI/Node.js 等）&lt;/li&gt;
&lt;li&gt;想优化后端性能、提高可扩展性的中级开发者&lt;/li&gt;
&lt;li&gt;对架构设计、异步系统感兴趣的工程师或技术负责人&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机"&gt;🎯 背景 / 动机&lt;/h2&gt;
&lt;p&gt;很多初学者写上传接口时会这样做：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/upload&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;upload_file&lt;/span&gt;(file: UploadFile):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; parse_and_store(file) &lt;span style="color:#75715e"&gt;# 阻塞操作&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;completed&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;表面简单，实则隐藏问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⏱ 超时风险高（解析/embedding/OCR可能几分钟）&lt;/li&gt;
&lt;li&gt;🧵 阻塞主线程，拖慢整个 API 服务&lt;/li&gt;
&lt;li&gt;💥 请求中断即任务丢失&lt;/li&gt;
&lt;li&gt;😕 用户只能干等着，无法看到进度&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解决方案就是：&lt;strong&gt;上传与处理分离&lt;/strong&gt;。上传只负责“投递任务”，处理由后台 worker 异步执行，状态存储在数据库中供前端查询。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;🔍 核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;异步任务（Async Job）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;文件解析、OCR、embedding 等耗时操作独立运行，不阻塞主线程。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;任务队列（Task Queue）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;临时存放待执行的任务，如 Redis、RabbitMQ、Celery。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;状态持久化（State Persistence）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;将任务状态（pending / processing / completed / failed）写入数据库。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE（Server-Sent Events）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一种轻量的实时推送机制，前端可实时接收状态更新。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南--实现步骤"&gt;⚙️ 实践指南 / 实现步骤&lt;/h2&gt;
&lt;h3 id="1-上传文件接口只负责入队"&gt;1️⃣ 上传文件接口（只负责入队）&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@router.post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/upload&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;upload&lt;/span&gt;(file: UploadFile, user&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Depends(get_verified_user)):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; file_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Files&lt;span style="color:#f92672"&gt;.&lt;/span&gt;create(file, user&lt;span style="color:#f92672"&gt;.&lt;/span&gt;id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 异步提交任务（Celery、RQ、线程池等）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; background_tasks&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add_task(process_file, file_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;file_id&amp;#34;&lt;/span&gt;: file_id, &lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;pending&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-异步任务后台-worker-执行"&gt;2️⃣ 异步任务（后台 worker 执行）&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;process_file&lt;/span&gt;(file_id: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; file &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Files&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(file_id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Files&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update_status(file_id, &lt;span style="color:#e6db74"&gt;&amp;#34;processing&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; parse_and_vectorize(file)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Files&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update_status(file_id, &lt;span style="color:#e6db74"&gt;&amp;#34;completed&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Files&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update_status(file_id, &lt;span style="color:#e6db74"&gt;&amp;#34;failed&amp;#34;&lt;/span&gt;, error&lt;span style="color:#f92672"&gt;=&lt;/span&gt;str(e))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-状态查询接口"&gt;3️⃣ 状态查询接口&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@router.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/process/status&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_status&lt;/span&gt;(id: str, stream: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; file &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Files&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; stream:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;event_stream&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; status &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Files&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_status(id)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;yield&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;data: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dumps({&lt;span style="color:#e6db74"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;: status})&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; status &lt;span style="color:#f92672"&gt;in&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#34;completed&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;failed&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; asyncio&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; StreamingResponse(event_stream(), media_type&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;text/event-stream&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: file&lt;span style="color:#f92672"&gt;.&lt;/span&gt;data&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;pending&amp;#34;&lt;/span&gt;)}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="-可运行示例"&gt;💻 可运行示例&lt;/h2&gt;
&lt;h3 id="前端轮询"&gt;前端轮询：&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;checkStatus&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;fileId&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;pending&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;pending&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;processing&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fetch&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`/api/files/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;fileId&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/process/status`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;当前状态:&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Promise(&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt; =&amp;gt; &lt;span style="color:#a6e22e"&gt;setTimeout&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;completed&amp;#39;&lt;/span&gt;) &lt;span style="color:#a6e22e"&gt;alert&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;解析完成！&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="前端-sse-实时监听"&gt;前端 SSE 实时监听：&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;evtSource&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;EventSource&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;`/api/files/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;fileId&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/process/status?stream=true`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;evtSource&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;onmessage&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; } &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;parse&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;文件状态:&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;completed&amp;#34;&lt;/span&gt;) &lt;span style="color:#a6e22e"&gt;evtSource&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="-原理解释与取舍"&gt;🧠 原理解释与取舍&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模式&lt;/th&gt;
&lt;th&gt;特点&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;同步上传+处理&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;实现简单，但阻塞主线程&lt;/td&gt;
&lt;td&gt;小文件、低并发、离线脚本&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;异步上传+状态查询（推荐）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;非阻塞、可恢复、可扩展&lt;/td&gt;
&lt;td&gt;Web 应用、后台任务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;消息队列驱动&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;支持分布式任务、重试机制&lt;/td&gt;
&lt;td&gt;大规模系统、微服务架构&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;取舍原则：&lt;/p&gt;</description></item><item><title>为什么让前端完成Chat Completion: 一套通用的多模型流式对话架构设计</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/frontend-chat-completion-multimodel-streaming-architecture/</link><pubDate>Sun, 02 Nov 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/frontend-chat-completion-multimodel-streaming-architecture/</guid><description>&lt;h1 id="-为什么让前端执行-chat-completion一套通用的多模型流式对话架构设计"&gt;🔌 为什么让前端执行 Chat Completion：一套通用的多模型流式对话架构设计&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;
在现代 AI 聊天系统中，很多人会问：为什么不直接在后端调用 OpenAI API？
本文将带你理解一种更灵活的架构——让前端承担推理执行，后端负责调度和状态同步。适合需要支持多模型、本地推理或用户自带 API Key 的开发者。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标读者&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI 聊天应用开发者&lt;/li&gt;
&lt;li&gt;WebSocket / Socket.IO 实践者&lt;/li&gt;
&lt;li&gt;想构建多模型、多端协作聊天系统的架构师&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机"&gt;🧠 背景 / 动机&lt;/h2&gt;
&lt;p&gt;传统的聊天后端往往直接在服务器调用 OpenAI API：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;resp &lt;span style="color:#f92672"&gt;=&lt;/span&gt; client&lt;span style="color:#f92672"&gt;.&lt;/span&gt;chat&lt;span style="color:#f92672"&gt;.&lt;/span&gt;completions&lt;span style="color:#f92672"&gt;.&lt;/span&gt;create(model&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;gpt-4o&amp;#34;&lt;/span&gt;, messages&lt;span style="color:#f92672"&gt;=&lt;/span&gt;messages)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虽然简单，但带来几个现实问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有请求都消耗服务器的 Key，成本高且难追踪；&lt;/li&gt;
&lt;li&gt;无法支持用户自定义 Key（BYOK 模式）；&lt;/li&gt;
&lt;li&gt;无法连接用户本地推理（如 Ollama、LM Studio）；&lt;/li&gt;
&lt;li&gt;无法切换不同模型或 API Base URL；&lt;/li&gt;
&lt;li&gt;前后端状态不同步，不利于流式消息推送。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了解决这些问题，一些开源系统（如 Open-WebUI、Chatbot-UI 增强版）采用了更灵活的 &lt;strong&gt;Socket.IO 双向通信架构&lt;/strong&gt;。
服务端负责「调度与状态流」，前端负责「执行与回传」。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;🧩 核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Socket.IO&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;基于 WebSocket 的实时双向通信库，支持事件与回调。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;event_emitter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;服务端向前端广播事件（推送消息/状态）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;event_caller (sio.call)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;服务端请求前端执行任务（RPC），并等待前端 callback 返回。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;request:chat:completion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一种自定义事件类型，用于请求前端执行 chat completion。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BYOK 模式&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;“Bring Your Own Key”，用户使用自己的 OpenAI Key 调用 API。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Executor 架构&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;前端承担推理任务的执行者，后端作为协调者。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南--步骤"&gt;🧭 实践指南 / 步骤&lt;/h2&gt;
&lt;h3 id="1-服务端发送调用请求"&gt;1️⃣ 服务端发送调用请求&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;res &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; event_caller({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;request:chat:completion&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;form_data&amp;#34;&lt;/span&gt;: form_data,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;: models[form_data[&lt;span style="color:#e6db74"&gt;&amp;#34;model&amp;#34;&lt;/span&gt;]],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;channel&amp;#34;&lt;/span&gt;: channel,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;session_id&amp;#34;&lt;/span&gt;: session_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里的 &lt;code&gt;event_caller&lt;/code&gt; 使用 &lt;code&gt;sio.call()&lt;/code&gt; 发送事件给指定客户端，并等待 callback 返回。&lt;/p&gt;</description></item><item><title>webSocket深入理解:为什么要保持一个永远在线的连接</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/websocket-why-keep-alive/</link><pubDate>Fri, 31 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/websocket-why-keep-alive/</guid><description>&lt;h1 id="-websocket-深入理解为什么要保持一个永远在线的连接"&gt;🛰️ WebSocket 深入理解：为什么要保持一个“永远在线”的连接？&lt;/h1&gt;
&lt;h2 id="-副标题--摘要"&gt;✨ 副标题 / 摘要&lt;/h2&gt;
&lt;p&gt;这篇文章带你彻底搞懂 WebSocket：
它和 HTTP 的根本区别、为什么需要“长连接”、连接是如何建立和保持的、以及它在实时应用中的意义。
适合想从“知道是什么”到“理解为什么”的开发者。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;👩‍💻 目标读者&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Web 前后端初级到中级开发者&lt;/li&gt;
&lt;li&gt;想实现实时聊天、AI 流式输出、协作系统的工程师&lt;/li&gt;
&lt;li&gt;想从 HTTP 模型过渡到实时架构思维的学习者&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机为什么这个问题重要"&gt;🧭 背景 / 动机：为什么这个问题重要？&lt;/h2&gt;
&lt;p&gt;几乎每个现代 Web 应用都涉及“实时”功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;聊天对话（ChatGPT、Slack）&lt;/li&gt;
&lt;li&gt;实时通知（邮箱、消息提醒）&lt;/li&gt;
&lt;li&gt;在线协作（Notion、Google Docs）&lt;/li&gt;
&lt;li&gt;数据看板（实时指标、监控）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然而，传统的 HTTP 是“一问一答”的协议，
无法满足&lt;strong&gt;服务器主动通知客户端&lt;/strong&gt;、&lt;strong&gt;低延迟双向通信&lt;/strong&gt;的需求。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WebSocket 的出现&lt;/strong&gt;，彻底改变了这种单向关系，
让 Web 应用第一次真正拥有了“实时对话”的能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念与术语解释"&gt;🧠 核心概念与术语解释&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一问一答型协议。客户端发请求，服务器回响应，然后断开。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;长连接&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一条保持不关闭的 TCP 连接，可反复收发数据。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WebSocket&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一种基于 TCP 的双向通信协议，能让服务器主动推送消息。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;握手 (Handshake)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;客户端通过 HTTP 请求告诉服务器：“我想升级为 WebSocket 协议”。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;帧 (Frame)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;WebSocket 传输的最小数据单元，比 HTTP header 更轻量。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;心跳 (Ping/Pong)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;定期发送的小数据包，防止连接超时断开。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南websocket-建立的全过程"&gt;🪜 实践指南：WebSocket 建立的全过程&lt;/h2&gt;
&lt;p&gt;1️⃣ &lt;strong&gt;浏览器发起请求（HTTP 阶段）&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>从 Pip 到 UV：一站式 Python 包管理与依赖同步指南</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pip-to-uv-python-package-management/</link><pubDate>Fri, 31 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/pip-to-uv-python-package-management/</guid><description>&lt;h1 id="-从-pip-到-uv一站式-python-包管理与依赖同步指南"&gt;🚀 从 Pip 到 UV：一站式 Python 包管理与依赖同步指南&lt;/h1&gt;
&lt;h2 id="-副标题--摘要"&gt;💡 副标题 / 摘要&lt;/h2&gt;
&lt;p&gt;想让你的 Python 环境更干净、更快、更可靠？本文将带你从传统的 &lt;code&gt;pip + venv + requirements.txt&lt;/code&gt; 迁移到现代的 &lt;code&gt;uv&lt;/code&gt; 包管理系统，并教你如何在两者之间无缝同步。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;🎯 目标读者&lt;/h2&gt;
&lt;p&gt;适合 &lt;strong&gt;Python 开发者&lt;/strong&gt;（初学者到中级）、&lt;strong&gt;数据科学家&lt;/strong&gt;、&lt;strong&gt;后端工程师&lt;/strong&gt;，以及希望提升开发环境一致性、减少依赖地狱的读者。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机"&gt;🔥 背景 / 动机&lt;/h2&gt;
&lt;p&gt;在日常 Python 开发中，我们经常遇到以下痛点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;环境混乱、包冲突；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip install&lt;/code&gt; 太慢；&lt;/li&gt;
&lt;li&gt;不同机器、团队成员环境不一致；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;requirements.txt&lt;/code&gt; 手动维护麻烦。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而 &lt;strong&gt;uv&lt;/strong&gt; 是一个由 Astral 团队推出的新一代包管理工具，
用 Rust 编写，集成了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;包安装（比 pip 快数倍）；&lt;/li&gt;
&lt;li&gt;虚拟环境管理；&lt;/li&gt;
&lt;li&gt;锁文件机制（可复现环境）；&lt;/li&gt;
&lt;li&gt;与 PyPI 完全兼容。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话：&lt;strong&gt;uv = pip + virtualenv + pip-tools + poetry 的融合体&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;🧩 核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;pyproject.toml&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;现代 Python 项目的依赖与元信息文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;uv.lock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;锁文件，记录所有依赖的精确版本，保证可复现&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;uv sync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;根据锁文件同步环境（自动创建/更新虚拟环境）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;uv add / remove&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;添加或删除依赖，并自动更新锁文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;uv export&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;导出为 &lt;code&gt;requirements.txt&lt;/code&gt;，兼容传统 pip 流程&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南--步骤"&gt;🛠 实践指南 / 步骤&lt;/h2&gt;
&lt;h3 id="一从-pip-项目迁移到-uv"&gt;一、从 pip 项目迁移到 uv&lt;/h3&gt;
&lt;p&gt;假设你已有一个项目：&lt;/p&gt;</description></item><item><title>对于ai系统的思考</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/thoughts-on-ai-systems/</link><pubDate>Fri, 31 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/thoughts-on-ai-systems/</guid><description>&lt;p&gt;对于一个系统来说，单线程就应该是一个助手，我们应该给每个用户就单纯提供一个助手，我们所需要做的就是优化这一个助手，&lt;/p&gt;
&lt;p&gt;绝对不是向一个用户可以提供很多个线程的处理方式，成本太高&lt;/p&gt;</description></item><item><title>如何高效审核后端fastapi代码</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/efficient-fastapi-code-review/</link><pubDate>Thu, 30 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/efficient-fastapi-code-review/</guid><description>&lt;h1 id="-如何高效审核-fastapi-后端项目的-pull-requestpr"&gt;🧩 如何高效审核 FastAPI 后端项目的 Pull Request（PR）&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要：&lt;/strong&gt;
本文为你系统梳理了在 Python FastAPI 项目中如何进行专业的代码审核流程，从逻辑正确性到安全、性能与架构一致性，附带实用审查清单与示例，助你成为团队中更高效的 Reviewer。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;👥 目标读者&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;strong&gt;Python + FastAPI&lt;/strong&gt; 的中高级后端开发者&lt;/li&gt;
&lt;li&gt;初入团队、需要学习代码审查流程的工程师&lt;/li&gt;
&lt;li&gt;负责代码质量与合并决策的 Tech Lead / Reviewer&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景与动机"&gt;💡 背景与动机&lt;/h2&gt;
&lt;p&gt;在多人协作的后端项目中，&lt;strong&gt;代码审查（Code Review）&lt;/strong&gt; 是保障系统稳定、提升团队代码质量的关键环节。
但许多工程师在面对 PR 时往往只“浏览一下改动”，忽略了逻辑、性能和安全的隐患。&lt;/p&gt;
&lt;p&gt;尤其在 &lt;strong&gt;FastAPI&lt;/strong&gt; 项目中，接口结构简洁、异步特性突出，但也因此容易出现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不当的 &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; 用法导致阻塞；&lt;/li&gt;
&lt;li&gt;不安全的输入校验；&lt;/li&gt;
&lt;li&gt;不一致的 Schema 与返回模型；&lt;/li&gt;
&lt;li&gt;难以维护的业务逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，本文将教你如何 &lt;strong&gt;系统化、标准化地审查 FastAPI PR&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;🧠 核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;概念&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PR (Pull Request)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;在 Git 平台上发起代码合并请求，等待他人审核后合并到主分支。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Review&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;同事间对代码进行质量和设计审查的过程。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FastAPI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;高性能、异步的 Python Web 框架，基于 Pydantic 和 Starlette。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pydantic Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FastAPI 的数据验证与序列化模型系统。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Depends()&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FastAPI 的依赖注入机制，用于数据库连接、认证等。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南pr-审核流程"&gt;🧭 实践指南：PR 审核流程&lt;/h2&gt;
&lt;h3 id="1-阅读-pr-描述"&gt;1️⃣ 阅读 PR 描述&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;明确改动目的、功能范围、对应 issue。&lt;/li&gt;
&lt;li&gt;判断是否为修复、功能新增、重构或优化。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-浏览改动文件"&gt;2️⃣ 浏览改动文件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;注意核心目录：&lt;code&gt;routers/&lt;/code&gt;, &lt;code&gt;schemas/&lt;/code&gt;, &lt;code&gt;models/&lt;/code&gt;, &lt;code&gt;services/&lt;/code&gt;, &lt;code&gt;core/&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;检查是否包含依赖变更、配置修改或多余文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-深入逻辑代码"&gt;3️⃣ 深入逻辑代码&lt;/h3&gt;
&lt;p&gt;重点审查：&lt;/p&gt;</description></item><item><title>如何配置gitea</title><link>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/configure-gitea/</link><pubDate>Tue, 28 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/configure-gitea/</guid><description>&lt;h1 id="-本地搭建-gitea打造你的私人-github含已有仓库导入指南"&gt;🚀 本地搭建 Gitea：打造你的私人 GitHub（含已有仓库导入指南）&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要：&lt;/strong&gt;
本文将手把手教你在本地电脑上安装轻量级 Git 服务器 —— Gitea。
无需 root、不会影响系统环境，让你像在 GitHub 一样管理、查看和推送项目，还能导入已有仓库。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标读者：&lt;/strong&gt;
👉 适合个人开发者、独立工程师、小型团队技术负责人。
适用于初中级开发者，有 Git 基础即可上手。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-背景--动机"&gt;🧠 背景 / 动机&lt;/h2&gt;
&lt;p&gt;很多开发者希望：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在公司电脑或内网环境下托管代码；&lt;/li&gt;
&lt;li&gt;不想使用云端（如 GitHub、Gitee）；&lt;/li&gt;
&lt;li&gt;又希望有 Web 界面、Pull Request、代码浏览体验。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但 &lt;strong&gt;GitLab 太重&lt;/strong&gt;（动辄占用数 GB 内存），而 Gitea 则：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🌱 轻量级、单可执行文件、支持 PR、Wiki、Issue、CI/CD。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;只需几分钟，你就能拥有一个完全属于自己的“小型 GitHub”。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;📘 核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitLab&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;功能最强大的开源 Git 平台，但资源占用高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gitea&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;轻量级自托管 Git 服务，界面类似 GitHub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bare 仓库&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;只保存版本数据、不包含工作区的纯仓库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pull Request&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一个分支向另一个分支发起的合并请求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gitea 默认使用的轻量数据库，无需额外配置&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南--安装步骤"&gt;🧩 实践指南 / 安装步骤&lt;/h2&gt;
&lt;h3 id="1-准备环境"&gt;1️⃣ 准备环境&lt;/h3&gt;
&lt;p&gt;系统要求：Linux / macOS / Windows 均可
推荐配置：内存 ≥ 512MB，磁盘 ≥ 1GB&lt;/p&gt;</description></item><item><title>掌握git提交规范,让团队协作更高效率</title><link>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-commit-conventions-team-efficiency/</link><pubDate>Sat, 25 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-commit-conventions-team-efficiency/</guid><description>&lt;h3 id="标题"&gt;&lt;strong&gt;标题&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;🚀 从「feat」到「fix」：掌握 Git 提交规范，让团队协作与自动化更高效&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="副标题--摘要"&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;一篇为开发者准备的实用指南，带你理解并掌握业界通行的 Git 提交信息标准（Conventional Commits），
从 commit 标签（如 &lt;code&gt;feat:&lt;/code&gt;、&lt;code&gt;fix:&lt;/code&gt;）到自动生成 changelog，一次学会写出高质量的提交记录。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="目标读者"&gt;&lt;strong&gt;目标读者&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;初学者&lt;/strong&gt;：刚开始使用 Git，想养成规范提交的习惯。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中级开发者&lt;/strong&gt;：希望让提交信息对团队和 CI 工具更友好。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;团队负责人 / 架构师&lt;/strong&gt;：想建立统一的代码提交标准，提升协作与版本管理效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="背景--动机"&gt;&lt;strong&gt;背景 / 动机&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;大多数开发者写提交信息的方式都是这样的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“update code”
“fix bug”
“修改东西”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这类信息短期可读，长期无用。
当团队人数增多、项目复杂时，&lt;strong&gt;无法追踪改动意图&lt;/strong&gt;，也无法让自动化工具正确识别变更类型。
这就是为什么业界推出了 &lt;strong&gt;Conventional Commits&lt;/strong&gt;：
一个简洁统一的 commit 语法标准，让 Git 提交&lt;strong&gt;可读、可追踪、可自动化&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="核心概念"&gt;&lt;strong&gt;核心概念&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Conventional Commits&lt;/strong&gt; 是一种提交信息格式约定，它规定了提交消息的结构：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;subject&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;footer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;type&lt;/code&gt;：提交类型，如 &lt;code&gt;feat&lt;/code&gt;、&lt;code&gt;fix&lt;/code&gt;、&lt;code&gt;docs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scope&lt;/code&gt;：作用范围，可选（如 &lt;code&gt;ui&lt;/code&gt;、&lt;code&gt;api&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subject&lt;/code&gt;：简短描述（不超过 50 字）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;body&lt;/code&gt;：详细说明（可选）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;footer&lt;/code&gt;：备注（如 BREAKING CHANGE）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="实践指南--步骤"&gt;&lt;strong&gt;实践指南 / 步骤&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1️⃣ &lt;strong&gt;设置 Git 编辑器为 Neovim（可选）&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>开启sshsystem进程失败怎么解决</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/fix-sshsystem-process-start-failure/</link><pubDate>Fri, 24 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/fix-sshsystem-process-start-failure/</guid><description>&lt;p&gt;&lt;strong&gt;标题：&lt;/strong&gt;
🚀 在无 sudo 环境下让 sshd 常驻运行：从错误排查到 nohup 与 systemd 双方案实战&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要：&lt;/strong&gt;
本文讲述如何在普通用户权限下运行 OpenSSH 服务，逐步解决“连接被拒绝”“密码认证失败”“systemd start-limit-hit”等典型问题，并最终用 nohup 与 systemd 实现持久运行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;目标读者：&lt;/strong&gt;
Linux 中级用户、科研或企业多用户服务器使用者、无 root 权限的 SSH 自部署者。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一背景--动机"&gt;一、背景 / 动机&lt;/h2&gt;
&lt;p&gt;在部分高校实验室或云主机环境中，普通账户没有 sudo 权限，默认 sshd 服务无法启动。
当我们需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;远程访问自己的 Linux 主机；&lt;/li&gt;
&lt;li&gt;使用 VS Code Remote 或 SCP 传文件；&lt;/li&gt;
&lt;li&gt;但又无法修改系统级配置；
就必须在&lt;strong&gt;用户态&lt;/strong&gt;自行运行 sshd。
然而这会引发一系列问题：端口冲突、防火墙、认证失败、&lt;code&gt;start-limit-hit&lt;/code&gt; 等。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="二核心概念"&gt;二、核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名称&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sshd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenSSH 守护进程，负责处理 SSH 登录请求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;用户态 sshd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;非 root 用户手动启动的 sshd 实例，仅有用户权限&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;authorized_keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;存放允许登录的公钥&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;nohup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;让程序脱离终端后台运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;systemd &amp;ndash;user&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;用户级 systemd 实例，可管理自启服务&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;start-limit-hit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;systemd 检测到服务频繁退出后自动暂停重启&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="三实践指南--全流程步骤"&gt;三、实践指南 / 全流程步骤&lt;/h2&gt;
&lt;h3 id="1-生成并配置-ssh-密钥"&gt;1️⃣ 生成并配置 SSH 密钥&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-keygen -t ed25519 -C &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; -f ~/.ssh/id_ed25519_noemail
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat ~/.ssh/id_ed25519_noemail.pub &amp;gt;&amp;gt; ~/.ssh/authorized_keys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chmod &lt;span style="color:#ae81ff"&gt;700&lt;/span&gt; ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chmod &lt;span style="color:#ae81ff"&gt;600&lt;/span&gt; ~/.ssh/authorized_keys
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;确保 &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; 权限正确。&lt;/p&gt;</description></item><item><title>没有sudo权限开启ssh</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/enable-ssh-without-sudo/</link><pubDate>Fri, 24 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/enable-ssh-without-sudo/</guid><description>&lt;p&gt;以下是一篇符合优秀技术博客规范的完整文章草稿，基于你上面完整的 SSH 启动与调试过程整理而成，适合发布到技术博客（如掘金、知乎专栏、Medium 或个人博客）。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="-在无-sudo-权限的-linux-环境下启动-ssh-服务用户态-sshd-全攻略"&gt;🧠 在无 &lt;code&gt;sudo&lt;/code&gt; 权限的 Linux 环境下启动 SSH 服务（用户态 sshd 全攻略）&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要：&lt;/strong&gt;
当你在学校机房、远程实验环境或受限服务器上没有 root 权限时，如何开启 SSH 服务并远程访问？本文从零带你在用户目录下运行可用的 &lt;code&gt;sshd&lt;/code&gt;，支持密钥登录并实现远程连接。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阅读时长：&lt;/strong&gt; 10 分钟
&lt;strong&gt;目标读者：&lt;/strong&gt; 中级 Linux 用户、科研人员、服务器使用者、DevOps 学习者
&lt;strong&gt;标签：&lt;/strong&gt; SSH、sshd、Linux、远程连接、非 root、系统配置
&lt;strong&gt;SEO 关键词：&lt;/strong&gt; SSH 无 root 权限、用户态 sshd、openssh 配置、非特权端口、远程登录失败&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-背景与动机"&gt;🎯 背景与动机&lt;/h2&gt;
&lt;p&gt;很多科研服务器、学校实验室或共享主机都不给普通用户 &lt;code&gt;sudo&lt;/code&gt; 权限。
然而我们仍常常需要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;远程登录自己的账户；&lt;/li&gt;
&lt;li&gt;上传/下载文件；&lt;/li&gt;
&lt;li&gt;或从另一台机器访问自己的进程。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;默认情况下，&lt;code&gt;sshd&lt;/code&gt; 服务需要 root 才能运行，因为它通常绑定在 22 端口并访问系统认证信息。但事实上，我们完全可以在 &lt;strong&gt;用户目录&lt;/strong&gt; 下运行一个“用户态 SSH 服务”，无需修改系统配置。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;🧩 核心概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;名词&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sshd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSH 服务端程序，负责接收和验证 SSH 连接。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;用户态（user-space）sshd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;普通用户自行启动的 sshd 进程，不使用 root 权限。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HostKey&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;服务器用于加密连接的密钥对。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AuthorizedKeys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;被允许登录该账户的公钥列表。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;/etc/shadow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;系统密码哈希存储文件，非 root 用户无法访问。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-实践指南从零启动用户态-ssh-服务"&gt;⚙️ 实践指南：从零启动用户态 SSH 服务&lt;/h2&gt;
&lt;h3 id="-第一步准备配置文件"&gt;🪜 第一步：准备配置文件&lt;/h3&gt;
&lt;p&gt;创建配置目录：&lt;/p&gt;</description></item><item><title>为什么可以Ping通却ssh不通?一次假ssh真vnc的排查过程</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/ping-works-ssh-fails-fake-ssh-true-vnc/</link><pubDate>Fri, 24 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/ping-works-ssh-fails-fake-ssh-true-vnc/</guid><description>&lt;h1 id="为什么能-ping-通却-ssh-不通一次假-ssh-真-vnc-的排查过程"&gt;&lt;strong&gt;为什么能 Ping 通却 SSH 不通？一次“假 SSH 真 VNC” 的排查过程&lt;/strong&gt;&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;副标题：&lt;/strong&gt; 从连接被拒到协议识别，带你彻底理解 TCP、SSH 与 VNC 的区别
&lt;strong&gt;阅读时长：&lt;/strong&gt; 7 分钟
&lt;strong&gt;标签：&lt;/strong&gt; 网络排查、SSH、VNC、Linux、远程连接
&lt;strong&gt;SEO 关键词：&lt;/strong&gt; SSH连接失败、kex_exchange_identification、VNC端口5905、RFB 003.008、SSH vs VNC&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;🎯 目标读者&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Linux 使用者、开发者、服务器维护人员&lt;/li&gt;
&lt;li&gt;想学习网络排错思路的中级工程师&lt;/li&gt;
&lt;li&gt;对 SSH/VNC 协议机制有兴趣的读者&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景与动机"&gt;💡 背景与动机&lt;/h2&gt;
&lt;p&gt;你是否遇到过这样的情况：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“服务器能 ping 通，但 SSH 连不上？”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这类问题很常见，尤其是在多服务（SSH、VNC、HTTP）混跑的远程主机上。
本文通过一次真实案例，展示从“SSH 连接失败”到“发现端口跑的是 VNC”的完整分析过程。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-问题现象"&gt;🔍 问题现象&lt;/h2&gt;
&lt;p&gt;执行命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh chenhm@101.6.142.82 -p &lt;span style="color:#ae81ff"&gt;5905&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kex_exchange_identification: Connection closed by remote host
Connection closed by 101.6.142.82 port 5905
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;尝试 &lt;code&gt;ping&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ping 101.6.142.82
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;能通，没有丢包。&lt;/p&gt;
&lt;p&gt;于是我们知道：&lt;/p&gt;</description></item><item><title>怎么撰写一篇完美的机器学习文档</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/how-to-write-a-perfect-ml-document/</link><pubDate>Fri, 24 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/how-to-write-a-perfect-ml-document/</guid><description>&lt;h1 id="-bengio-风格的机器学习任务说明文档从研究到工程的技术规范指南"&gt;🧠 Bengio 风格的机器学习任务说明文档：从研究到工程的技术规范指南&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题：&lt;/strong&gt;
如何编写一份可复现、可解释、可比较的模型微调任务说明文档 —— 来自 Yoshua Bengio 的研究方法论
&lt;strong&gt;阅读时长：&lt;/strong&gt; 10 分钟
&lt;strong&gt;标签：&lt;/strong&gt; &lt;code&gt;机器学习文档结构&lt;/code&gt; &lt;code&gt;模型微调&lt;/code&gt; &lt;code&gt;技术规范&lt;/code&gt; &lt;code&gt;深度学习实践&lt;/code&gt;
&lt;strong&gt;适合读者：&lt;/strong&gt; 中高级 ML 工程师、研究员、技术写作者&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一为什么需要这样的文档"&gt;一、为什么需要这样的文档？&lt;/h2&gt;
&lt;p&gt;在机器学习项目中，我们经常遇到这样的情况：
团队完成了一个模型微调实验，但几个月后再回头看，没人能完全复现结果，也不清楚为什么要采用某个学习率或 LoRA 层。&lt;/p&gt;
&lt;p&gt;Yoshua Bengio（深度学习三巨头之一）早在 &lt;em&gt;Montréal Institute for Learning Algorithms (MILA)&lt;/em&gt; 就提出了一个理念：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“一个机器学习研究或工程任务的文档，必须能让他人完全重现结果并理解背后的设计动机。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是后来被称为 &lt;strong&gt;Bengio-style Machine Learning Project Report Structure&lt;/strong&gt; 的经典模板，被 Google Research、Meta AI、OpenAI 等广泛采用。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二bengio-风格模板的核心思想"&gt;二、Bengio 风格模板的核心思想&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;项目&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;来源&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yoshua Bengio，《Deep Learning Research Practice Notes》&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;目标&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;确保机器学习实验 &lt;strong&gt;可复现&lt;/strong&gt;、&lt;strong&gt;可理解&lt;/strong&gt;、&lt;strong&gt;可比较&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;适用场景&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;模型微调、对比实验、学术研究报告、内部技术说明&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;优势&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;逻辑清晰、结构统一、可直接转化为论文或内部白皮书&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="三标准结构适用于四个模型微调任务"&gt;三、标准结构（适用于四个模型微调任务）&lt;/h2&gt;
&lt;p&gt;以下是 Bengio 风格文档的经典九个部分：&lt;/p&gt;</description></item><item><title>如何配置orm管理数据</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/configure-orm-data-management/</link><pubDate>Thu, 23 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/configure-orm-data-management/</guid><description>&lt;h1 id="-从零到生产如何优雅地设计-orm-层管理以-sqlalchemy-为核心"&gt;🧱 从零到生产：如何优雅地设计 ORM 层管理（以 SQLAlchemy 为核心）&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;本文将带你从数据库表结构出发，构建一套高内聚、低耦合的 ORM 层架构。
目标：让你的 Flask / FastAPI 项目在数据访问上既简洁又稳健。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="一为什么要重视-orm-层设计"&gt;一、为什么要重视 ORM 层设计？&lt;/h2&gt;
&lt;p&gt;很多项目初期只是“先能跑”，直接把 SQL 写在控制器里，但很快就会出现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;业务逻辑和 SQL 混在一起；&lt;/li&gt;
&lt;li&gt;表关系复杂，维护困难；&lt;/li&gt;
&lt;li&gt;想复用查询逻辑很麻烦；&lt;/li&gt;
&lt;li&gt;迁移到别的框架（Flask → FastAPI）代价大。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ORM 层（Object Relational Mapping）是数据库与业务逻辑之间的 &lt;strong&gt;抽象桥梁&lt;/strong&gt;，
一个好的 ORM 层能让你只关心对象，不用反复写 SQL。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二项目场景招标信息数据系统"&gt;二、项目场景：招标信息数据系统&lt;/h2&gt;
&lt;p&gt;我们以一个真实业务为例：
爬取各网站的招标公告，保存为结构化数据，并生成统计看板。&lt;/p&gt;
&lt;h3 id="目标数据库实体"&gt;目标数据库实体&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;表名&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tender_info&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;公告基本信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tender_attachments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;公告及变更文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tender_organization&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;招标机构与联系方式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tender_statistics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;每日/月/年统计信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="三orm-层设计思路"&gt;三、ORM 层设计思路&lt;/h2&gt;
&lt;h3 id="-分层原则"&gt;🧩 分层原则&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层级&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;th&gt;代码位置&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model 层&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ORM 模型定义，对应数据库表结构&lt;/td&gt;
&lt;td&gt;&lt;code&gt;models.py&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repository 层&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;封装 CRUD 逻辑（数据库操作）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;repository.py&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service 层&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;业务逻辑层（聚合多个仓库逻辑）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;service.py&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API 层&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;控制器/路由接口&lt;/td&gt;
&lt;td&gt;Flask/FastAPI 视图文件&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这种分层让你做到：&lt;/p&gt;</description></item><item><title>在Ubuntu上让frp内网穿透服务开机自启</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/frp-auto-start-on-ubuntu/</link><pubDate>Thu, 23 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/frp-auto-start-on-ubuntu/</guid><description>&lt;h1 id="-在-ubuntu-上让-frp-内网穿透服务开机自启完整指南"&gt;🚀 在 Ubuntu 上让 frp 内网穿透服务开机自启：完整指南&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;副标题 / 摘要&lt;/strong&gt;
通过 systemd 将 frp（Fast Reverse Proxy）设置为系统服务，实现稳定、安全、可监控的开机自动启动方案，避免每次手动运行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阅读时长&lt;/strong&gt;：8 分钟
&lt;strong&gt;标签&lt;/strong&gt;：frp、内网穿透、systemd、自启、Linux、Ubuntu
&lt;strong&gt;SEO 关键词&lt;/strong&gt;：frp 开机自启、Ubuntu frp 配置、frpc systemd、frps 服务端启动、内网穿透配置
&lt;strong&gt;元描述&lt;/strong&gt;：手把手教你在 Ubuntu 上使用 systemd 将 frp（frpc / frps）设置为开机自启服务，附配置文件模板与常见问题排查。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-目标读者"&gt;🎯 目标读者&lt;/h2&gt;
&lt;p&gt;适合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想在云服务器上部署 frps 的开发者&lt;/li&gt;
&lt;li&gt;想让家中/办公内网机器长期稳定穿透的中级 Linux 用户&lt;/li&gt;
&lt;li&gt;DevOps / 自建服务爱好者&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-背景与动机"&gt;🧩 背景与动机&lt;/h2&gt;
&lt;p&gt;许多开发者使用 &lt;strong&gt;frp&lt;/strong&gt; 实现内网穿透，让内网服务（如 SSH、Web、NAS）可以安全地从外部访问。
问题是：手动运行 &lt;code&gt;./frpc -c frpc.ini&lt;/code&gt; 既麻烦又不稳定，机器重启后容易忘记启动。&lt;/p&gt;
&lt;p&gt;因此，我们希望通过 &lt;strong&gt;systemd 服务&lt;/strong&gt; 实现“&lt;strong&gt;自动随系统启动 + 失败自动重启 + 集中日志管理&lt;/strong&gt;”的效果。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-核心概念"&gt;💡 核心概念&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;frps / frpc&lt;/strong&gt;：frp 的服务端与客户端可执行程序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;systemd&lt;/strong&gt;：现代 Linux 系统的服务管理器，用于定义和控制后台服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;unit 文件&lt;/strong&gt;：定义服务的配置（如启动命令、依赖、重启策略）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-实践步骤指南"&gt;🛠️ 实践步骤指南&lt;/h2&gt;
&lt;h3 id="1-安装与准备"&gt;1️⃣ 安装与准备&lt;/h3&gt;
&lt;p&gt;将二进制文件与配置文件放入系统路径：&lt;/p&gt;</description></item><item><title>WSL解决内网和windows不共享</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/wsl-intranet-not-shared-with-windows/</link><pubDate>Wed, 22 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/wsl-intranet-not-shared-with-windows/</guid><description>&lt;h1 id="-windows--wsl2-端口转发教程访问-flask-5000"&gt;📝 Windows + WSL2 端口转发教程（访问 Flask 5000）&lt;/h1&gt;
&lt;h2 id="前提条件"&gt;前提条件&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;你正在使用 &lt;strong&gt;WSL2&lt;/strong&gt;（Ubuntu 或其他 Linux 发行版）&lt;/li&gt;
&lt;li&gt;Windows 主机能访问局域网（Wi-Fi 或以太网）&lt;/li&gt;
&lt;li&gt;Flask 服务在 WSL2 中运行，并监听：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run(host&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;, port&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;code&gt;host=&amp;quot;0.0.0.0&amp;quot;&lt;/code&gt; 必须，否则外部无法访问&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="第-1-步确认-wsl2-的-ip"&gt;第 1 步：确认 WSL2 的 IP&lt;/h2&gt;
&lt;p&gt;在 WSL2 中运行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ip addr show eth0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你会看到类似：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;inet 172.26.209.37/20
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;记下 &lt;code&gt;inet&lt;/code&gt; 后面的 IP（本例是 &lt;code&gt;172.26.209.37&lt;/code&gt;），这是 WSL2 内部 IP。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="第-2-步打开-powershell管理员模式"&gt;第 2 步：打开 PowerShell（管理员模式）&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;按 &lt;code&gt;Win + X&lt;/code&gt; → 选择 &lt;strong&gt;Windows PowerShell (管理员)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;确认管理员权限，必要时允许 UAC 提示&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="第-3-步设置端口转发"&gt;第 3 步：设置端口转发&lt;/h2&gt;
&lt;p&gt;在 PowerShell 中执行以下命令，将 Windows 的 5000 端口转发到 WSL2：&lt;/p&gt;</description></item><item><title>局域网Git Bare</title><link>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/lan-git-bare-repo/</link><pubDate>Wed, 22 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/lan-git-bare-repo/</guid><description>&lt;h1 id="在局域网访问-windows-wsl2-上的-git-bare-仓库"&gt;在局域网访问 Windows WSL2 上的 Git Bare 仓库&lt;/h1&gt;
&lt;p&gt;在开发中，我们经常需要在多台电脑之间共享 Git 仓库。如果你在 Windows 上使用 WSL2，并且想在同一局域网的其他电脑访问 WSL2 上的 Git bare 仓库，本文将一步步教你实现。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-在-wsl2-创建-git-bare-仓库"&gt;1. 在 WSL2 创建 Git Bare 仓库&lt;/h2&gt;
&lt;p&gt;打开 WSL2 终端，进入你想存放仓库的目录，执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git init --bare my_project.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;my_project.git&lt;/code&gt; 是 bare 仓库，不含工作区，仅用于推送和拉取。&lt;/li&gt;
&lt;li&gt;bare 仓库就像远程仓库一样，可以被克隆和操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="2-配置-wsl2-的-ssh-服务"&gt;2. 配置 WSL2 的 SSH 服务&lt;/h2&gt;
&lt;p&gt;为了让其他电脑访问仓库，需要通过 SSH 访问 WSL2。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;安装 SSH 服务：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt install openssh-server -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;启动 SSH 服务：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo service ssh start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;检查 SSH 服务状态：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo service ssh status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="4"&gt;
&lt;li&gt;默认端口是 22，可以在 &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; 修改。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="3-获取-wsl2-ip-地址"&gt;3. 获取 WSL2 IP 地址&lt;/h2&gt;
&lt;p&gt;在 WSL2 终端运行：&lt;/p&gt;</description></item><item><title>如何使用wrk进行压测</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/wrk-load-testing-guide/</link><pubDate>Wed, 22 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/wrk-load-testing-guide/</guid><description>&lt;h1 id="-使用-wrk-对接口进行高性能压力测试超详细教程"&gt;🚀 使用 wrk 对接口进行高性能压力测试（超详细教程）&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;本文介绍如何在 Ubuntu 环境中使用 &lt;code&gt;wrk&lt;/code&gt; 对后端接口（如 Flask / FastAPI / Spring Boot 等）进行高并发压力测试，并结合结果分析性能瓶颈。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="-一什么是-wrk"&gt;🧰 一、什么是 wrk？&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/wg/wrk"&gt;&lt;code&gt;wrk&lt;/code&gt;&lt;/a&gt; 是一个现代化、高性能的 HTTP 压测工具，由 C 语言编写，具有以下特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高并发能力强&lt;/strong&gt;：支持成千上万的并发连接&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;支持多线程&lt;/strong&gt;：充分利用多核 CPU&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可自定义 Lua 脚本&lt;/strong&gt;：适合复杂场景（如自定义请求头、Body、Token 等）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;比 Apache Benchmark (ab)&lt;/strong&gt; 更轻量、更快、更稳定&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-二安装-wrk"&gt;⚙️ 二、安装 wrk&lt;/h2&gt;
&lt;p&gt;在 Ubuntu / Debian 上安装：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt install wrk -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证安装是否成功：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wrk --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出类似：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;wrk 4.2.0 [epoll]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;表示安装成功 ✅&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-三快速开始压测"&gt;🧪 三、快速开始压测&lt;/h2&gt;
&lt;p&gt;假设你的服务运行在：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;http://192.168.1.224:5000/api/tenders
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;运行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wrk -t4 -c100 -d30s http://192.168.1.224:5000/api/tenders
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="参数说明"&gt;参数说明：&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-t4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;启动 4 个线程（利用多核 CPU）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-c100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;模拟 100 个并发连接&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-d30s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;持续压测 30 秒&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;最后一个参数&lt;/td&gt;
&lt;td&gt;目标 URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="-四示例输出结果解读"&gt;📊 四、示例输出结果解读&lt;/h2&gt;
&lt;p&gt;假设输出如下：&lt;/p&gt;</description></item><item><title>git分支管理方法</title><link>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-branching-workflow/</link><pubDate>Mon, 20 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-branching-workflow/</guid><description>&lt;h1 id="-简化-git-分支工作流个人--小团队"&gt;🌿 简化 Git 分支工作流（个人 / 小团队）&lt;/h1&gt;
&lt;p&gt;本工作流基于 Git Flow 精简而来，适合个人或小团队，既规范又不复杂。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-1-主分支长期分支"&gt;🚀 1. 主分支（长期分支）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;main&lt;/code&gt;&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;永远保持稳定、可发布的状态。&lt;/li&gt;
&lt;li&gt;部署到生产环境的代码都来自这里。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;对于小团队，通常只需要 &lt;code&gt;main&lt;/code&gt;，不需要维护 &lt;code&gt;develop&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="-2-功能开发feature-branch"&gt;🛠️ 2. 功能开发（Feature Branch）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;分支命名：&lt;code&gt;feature/&amp;lt;功能名&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;用途：开发新功能，完成后合并回 &lt;code&gt;main&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;
feature/login-api
feature/user-profile
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;流程：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 从 main 创建功能分支&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout -b feature/login-api main
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 开发完成后，合并到 main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout main
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git merge feature/login-api
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git branch -d feature/login-api
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="-3-bug-修复bugfix-branch"&gt;🐞 3. Bug 修复（Bugfix Branch）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;分支命名：&lt;code&gt;bugfix/&amp;lt;问题名&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;用途：修复测试或开发环境的 bug。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;bugfix/fix-login-redirect
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;流程同 feature 分支，完成后合并回 &lt;code&gt;main&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-4-紧急修复hotfix-branch"&gt;🔥 4. 紧急修复（Hotfix Branch）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;分支命名：&lt;code&gt;hotfix/&amp;lt;问题名&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;用途：生产环境出现严重问题时的快速修复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;</description></item><item><title>在本地使用git裸仓库实现开发环境和测试环境隔离</title><link>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-bare-repo-dev-test-isolation/</link><pubDate>Mon, 20 Oct 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/notes/git-notes/git-bare-repo-dev-test-isolation/</guid><description>&lt;h1 id="在本地使用-git-裸仓库实现开发环境和测试环境隔离"&gt;在本地使用 Git 裸仓库实现开发环境和测试环境隔离&lt;/h1&gt;
&lt;p&gt;在全栈开发的过程中，我们常常遇到一个问题：&lt;strong&gt;开发环境和测试环境如何隔离&lt;/strong&gt;？
很多人第一反应是用 GitHub 或 GitLab 来托管代码，但如果项目涉及隐私，不方便放在公共仓库，那该怎么办呢？&lt;/p&gt;
&lt;p&gt;其实，Git 是分布式的，我们完全可以在 &lt;strong&gt;本地电脑上建立一个“裸仓库 (bare repo)”&lt;/strong&gt;，当作“远程仓库”来用，从而实现 &lt;strong&gt;开发环境 → 测试环境&lt;/strong&gt; 的代码迁移和同步。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="什么是裸仓库-bare-repository"&gt;什么是裸仓库 (bare repository)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;普通 Git 仓库（&lt;code&gt;git init&lt;/code&gt;）包含 &lt;strong&gt;工作区 + .git 元数据&lt;/strong&gt;，可以直接编辑文件。&lt;/li&gt;
&lt;li&gt;裸仓库（&lt;code&gt;git init --bare&lt;/code&gt;）只有 Git 的版本信息，没有工作区，不能直接编辑文件，通常作为 &lt;strong&gt;远程仓库&lt;/strong&gt; 来存储和同步代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;简单理解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;开发仓库&lt;/strong&gt;：我在这里写代码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;裸仓库&lt;/strong&gt;：我用来存放代码历史，作为远程同步点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;测试仓库&lt;/strong&gt;：从裸仓库克隆出来，模拟运行环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="步骤一创建裸仓库"&gt;步骤一：创建裸仓库&lt;/h2&gt;
&lt;p&gt;在本机某个目录（比如 &lt;code&gt;~/.repos&lt;/code&gt;）下创建裸仓库：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p ~/.repos
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd ~/.repos
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git init --bare scrapy.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样你得到一个路径 &lt;code&gt;~/.repos/scrapy.git&lt;/code&gt;，它就是本地的远程仓库。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="步骤二在开发仓库里添加远程"&gt;步骤二：在开发仓库里添加远程&lt;/h2&gt;
&lt;p&gt;假设你的开发仓库在 &lt;code&gt;~/scrapy&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd ~/scrapy
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git remote add local ~/.repos/scrapy.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;检查一下远程是否添加成功：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git remote -v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出类似：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;local /home/gong/.repos/scrapy.git (fetch)
local /home/gong/.repos/scrapy.git (push)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;说明配置成功。&lt;/p&gt;</description></item><item><title>结构化日志和追踪</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/python/structured-logging-and-tracing/</link><pubDate>Thu, 28 Aug 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/python/structured-logging-and-tracing/</guid><description>&lt;p&gt;Python 日志与追踪
Python 日志追踪实践
结构化日志与追踪&lt;/p&gt;
&lt;p&gt;副标题/摘要：
结合 logging + OpenTelemetry 实现结构化日志并把 trace_id 注入日志，便于在生产环境串联调用链与定位问题。&lt;/p&gt;
&lt;p&gt;TL;DR：
设置 json 格式日志并通过 OpenTelemetry 在每条日志里注入 trace_id/span_id。关键步骤：安装依赖 → 配置 logging（JSON）→ 配置 TracerProvider → 用 Filter 从当前 span 提取 trace 信息并添加到日志记录中。&lt;/p&gt;
&lt;p&gt;目录&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;背景与动机（为什么需要）&lt;/li&gt;
&lt;li&gt;关键概念与术语解释&lt;/li&gt;
&lt;li&gt;环境与依赖（安装命令）&lt;/li&gt;
&lt;li&gt;逐步实战示例（可直接运行）&lt;/li&gt;
&lt;li&gt;原理与实现要点&lt;/li&gt;
&lt;li&gt;常见问题与注意事项&lt;/li&gt;
&lt;li&gt;最佳实践总结&lt;/li&gt;
&lt;li&gt;结论与下一步建议&lt;/li&gt;
&lt;li&gt;可视化建议&lt;/li&gt;
&lt;li&gt;参考与延伸阅读&lt;/li&gt;
&lt;li&gt;可复制示例代码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;背景与动机（为什么需要）
现代后端服务分布式部署后，单靠文本日志很难把一次请求链路从入口到后端串起来。结构化日志（JSON）便于聚合与查询；而分布式追踪（tracing）给出调用链与 span 信息。二者结合能快速定位延迟与错误根因：日志告诉你“发生了什么”，trace 告诉你“这个请求经过了哪些服务/操作”。&lt;/p&gt;
&lt;p&gt;关键概念与术语解释（简明）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;日志（Logging）：程序运行时的事件记录，通常按级别（INFO/ERROR）输出。&lt;/li&gt;
&lt;li&gt;结构化日志：以 JSON 等结构化格式输出，便于机器处理与检索。&lt;/li&gt;
&lt;li&gt;Trace/Span：一次分布式操作（trace）由若干子操作（span）组成，span 含有 trace_id 与 span_id。&lt;/li&gt;
&lt;li&gt;Context Propagation：在不同服务/线程/协程中传递 trace context 以串联调用链。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;环境与依赖（列出安装命令）
推荐环境：Python 3.8+
安装依赖：
pip install python-json-logger opentelemetry-api opentelemetry-sdk&lt;/p&gt;</description></item><item><title>如何使用和配置typescript环境</title><link>https://jeanphilogong.github.io/jeanblog/zh/dev/frontend/typescript-setup-guide/</link><pubDate>Thu, 28 Aug 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/dev/frontend/typescript-setup-guide/</guid><description>&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;对于typescript以.ts为后缀的文件,我们是不能直接编译运行的,我们需要把typescript文件转译为js文件然后再进行运行&lt;/p&gt;
&lt;p&gt;我们可以选择两种方式,把ts文件传到服务器上使用ci工具进行编译,或者直接在本地转译后上传js文件到生产环境,如果我们想要在开发环境中直接进行运行测试的话,可以使用ts-node进行运行,但是开发环境之中还是需要编译&lt;/p&gt;</description></item><item><title>关于ai助手前端界面的新构建方案构思</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/ai-assistant-frontend-rebuild-ideas/</link><pubDate>Wed, 27 Aug 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/ai-assistant-frontend-rebuild-ideas/</guid><description>&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;我现在想要构建一个可以树状,或者图状进行问答的ai系统,而不是传统的单线式对话流程&lt;/p&gt;
&lt;h1 id="探索"&gt;探索&lt;/h1&gt;
&lt;h2 id="开源框架探索"&gt;开源框架探索&lt;/h2&gt;
&lt;h3 id="flowise"&gt;flowise&lt;/h3&gt;</description></item><item><title>mastering paper</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/mastering-paper/</link><pubDate>Tue, 26 Aug 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/mastering-paper/</guid><description>&lt;h1 id="如何尽可能掌握一篇论文中的所有知识"&gt;如何尽可能掌握一篇论文中的所有知识&lt;/h1&gt;
&lt;h1 id="结论"&gt;结论&lt;/h1&gt;
&lt;p&gt;我们要真正&amp;quot;掌握&amp;quot;一篇论文,不是读一遍就行,而是按照现有结构把论文进行拆解,验证,重构并把关键点转化为你自己的表述或者实现.目标是:可以在5分钟内讲清楚核心贡献,可以手推关键公式,可以实现并复现一个核心实验&lt;/p&gt;
&lt;h1 id="原理和背景"&gt;原理和背景&lt;/h1&gt;
&lt;p&gt;论文是作者对问题的压缩表达：省略背景、实验细节、直觉和失败。要掌握，需要把这种高密度信息“解压”回你自己的知识网络：理解背景假设、数学推导、工程实现、以及结论的适用范围。这样才能判断什么时候能用，什么时候不能用，什么时候要改进。&lt;/p&gt;
&lt;h1 id="具体步骤"&gt;具体步骤&lt;/h1&gt;
&lt;p&gt;不要把论文当成“权威”，把它当成一个可以测验的主张：把声明分解成可验证的小断言，然后去验证它。掌握不是记住论文的文字，而是把它变为你自己能用的工具。不要偷懒 — 真正的理解需要做事：推导、实现、对比、解释。现在就挑一篇，按上面的三天计划开始。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;准备与预读（30–60 分钟）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;读题目、摘要、结论、图表（不必细读正文）。目的：抓住“这篇论文到底解决了什么问题、给出了什么结果”。&lt;/li&gt;
&lt;li&gt;快速扫一遍引言和贡献列表，记录作者声称的三个关键点。&lt;/li&gt;
&lt;li&gt;检查参考文献，确定是否需要补读哪些基础材料（比如某个经典算法或证明）。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;精读（2–6 小时）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;逐段细读方法/理论部分。遇到公式，尝试手推关键推导（用纸和笔）。&lt;/li&gt;
&lt;li&gt;把每个重要符号写成表格，免得混淆。对算法，写伪代码。&lt;/li&gt;
&lt;li&gt;标注不理解/可疑的地方，形成问题清单。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;解构与重构（半天到几天）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;把论文分解为：问题定义、关键假设、方法/算法、主要定理、实验设置、结论与限制。&lt;/li&gt;
&lt;li&gt;为每一部分写一段 2–3 句的“我能讲给同领域的人”的解释（用你自己的话）。&lt;/li&gt;
&lt;li&gt;将算法实现为最小可运行版本（See 实现建议）。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;实现与复现（几小时到几天）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;优先实现最能体现贡献的部分（一个算法/一个模型/一个关键实验）。&lt;/li&gt;
&lt;li&gt;用小规模合成数据先做调试，再跑论文的设置。&lt;/li&gt;
&lt;li&gt;必要工具/模板示例：&lt;/li&gt;
&lt;li&gt;推荐环境：Python + Jupyter/Colab，或 C++/Rust（如果是系统/性能论文）。&lt;/li&gt;
&lt;li&gt;常用库：numpy/pandas/matplotlib/scikit-learn/torch/tensorflow。&lt;/li&gt;
&lt;li&gt;示例：把论文算法写成 Python 函数（伪代码转实现）。&lt;/li&gt;
&lt;li&gt;逐行注释已写在函数 docstring 和代码中。把论文中的符号映射到代码变量，记录在注释里。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;绘图与结果对比&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;重现关键图表（训练曲线、误差表）。如果不能一次跑出论文结果，先验证趋势和相对对比（例如比基线高多少）。&lt;/li&gt;
&lt;li&gt;加入断言和单元测试：例如，对已知问题（合成数据）的行为应与理论一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="6"&gt;
&lt;li&gt;消化与输出（持续）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;把关键点写成一页“cheatsheet”或一篇短博客，目标：在五分钟内让人理解。&lt;/li&gt;
&lt;li&gt;将难点做成 Anki 卡片（问题：关键假设、定理条件、公式推导步骤）。&lt;/li&gt;
&lt;li&gt;尝试解释给陌生人或写读书报告。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="7"&gt;
&lt;li&gt;工具推荐（实操）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;文献管理：Zotero / Mendeley&lt;/li&gt;
&lt;li&gt;笔记与知识库：Obsidian / Notion / org-mode&lt;/li&gt;
&lt;li&gt;代码与实验：Git + Jupyter/Colab + Docker（必要时复现环境）&lt;/li&gt;
&lt;li&gt;文本处理：pdftotext、pdfgrep、grep、ripgrep&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="常见错误"&gt;常见错误&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;错误：只读不做（只看结论，不推导、不实现）。&lt;/li&gt;
&lt;li&gt;调试：强制自己实现或至少写伪代码并手推一遍。&lt;/li&gt;
&lt;li&gt;错误：忽视假设/边界条件（在不满足假设的地方直接使用方法）。
调试：列出所有假设，构造违反假设的测试用例，观察失败模式。&lt;/li&gt;
&lt;li&gt;错误：把作者的实现等同于论文中的方法（代码细节、超参常被省略）。
调试：阅读作者代码（如开源），比对论文描述，记录差异。&lt;/li&gt;
&lt;li&gt;错误：过早追求论文结果的数值精确复现。
调试：先验证可复制的趋势，再逐步细化超参/实现细节。&lt;/li&gt;
&lt;li&gt;错误：数学推导只看结论公式，未验证每一步是否合法。
调试：逐行手推，找出隐含步骤或引用的引理，补读来源。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="验证方法"&gt;验证方法&lt;/h1&gt;
&lt;p&gt;能在五分钟内口述论文的核心贡献、适用场景与限制（不看稿）。
能手动推导关键公式或重写证明的主要步骤（纸笔完成）。
能实现一个最小工作例子，得到与论文一致的趋势或数值（至少在合成数据上）。
能回答以下问题：作者的关键假设是什么？结果如何依赖这些假设？有哪些潜在失败模式？
能把论文的想法应用到一个稍有不同的问题上并观察结果（迁移能力）。&lt;/p&gt;</description></item><item><title>如何创建mermaid图像并进行编辑</title><link>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/create-and-edit-mermaid-diagrams/</link><pubDate>Tue, 26 Aug 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/linux/linux/create-and-edit-mermaid-diagrams/</guid><description>&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;Mermaid是一个用于使用代码创建图像的框架,今天的博客,我们将会简单介绍如何在自己的服务器上安装相关的框架,并对代码进行渲染生成图像&lt;/p&gt;
&lt;h1 id="具体步骤"&gt;具体步骤&lt;/h1&gt;
&lt;h2 id="如何安装渲染框架"&gt;如何安装渲染框架&lt;/h2&gt;
&lt;p&gt;使用&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install -g @mermaid-js/mermaid-cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就可以安装&lt;/p&gt;
&lt;p&gt;需要注意的是该框架使用的npm版本需要大于20,所以我们需要切换npm版本,推荐使用nvm管理npm的版本&lt;/p&gt;
&lt;p&gt;如果没有nvm的话,使用下列命令进行安装&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -o https://raw.githubusercontent.com/nvm-sh/nvim/v0.39.4/install.sh | bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后对shell进行重启&lt;/p&gt;
&lt;p&gt;然后使用&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nvm install &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nvm use &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nvm alias default &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;进行安装,并把默认npm切换为20&lt;/p&gt;
&lt;p&gt;可以使用&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;node -v
npm -v
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;确认版本&lt;/p&gt;
&lt;h2 id="如何进行渲染"&gt;如何进行渲染&lt;/h2&gt;
&lt;p&gt;将需要渲染的代码放置在以.mmd结尾的文件中&lt;/p&gt;
&lt;p&gt;然后使用&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mmdc -i diagrams/example.mmd -o images/example.svg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;即可&lt;/p&gt;</description></item><item><title>阅读nvidia小模型理论论文</title><link>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/reading-nvidia-small-models-paper/</link><pubDate>Tue, 26 Aug 2025 00:00:00 +0000</pubDate><guid>https://jeanphilogong.github.io/jeanblog/zh/thoughts/thoughts/reading-nvidia-small-models-paper/</guid><description>&lt;h1 id="这篇论文解决了什么问题给出了什么结果"&gt;这篇论文解决了什么问题,给出了什么结果&lt;/h1&gt;
&lt;p&gt;首先我们知道AI系统现在广阔发展,可以像人类一样解决很多通用问题,但是现在发展中的ai agent系统所制作的大量应用作用于一些很小的任务,然后nvidia在这篇文献中提出了小语言模型(SLMs) 有着足够的能力,更适合,而且也更廉价,对于很多agent系统,也应该作为后来ai agent的一个主要发展方向&lt;/p&gt;
&lt;p&gt;然后针对与其提出的这个论点,该论文进行了以下几点讨论
1.当前小语言模型可以做到的任务
2.在某些通用语言能力是重要的部分
3.讨论了小模型作为agent系统的潜力界限&lt;/p&gt;
&lt;p&gt;结论,介绍了不管是从能力还是经济价值方面,从LLMs移动到SLMs的优势&lt;/p&gt;</description></item></channel></rss>