<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Yet Another Blog</title>
    <description>又一个不怎么更新的博客，纯面向自己写作 :)
</description>
    <link>https://yangcongchufang.com/</link>
    <atom:link href="https://yangcongchufang.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 28 Sep 2026 14:56:09 +0800</pubDate>
    <lastBuildDate>Mon, 28 Sep 2026 14:56:09 +0800</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>我以为 TanStack 只是前端，直到我看到它直接查 PostgreSQL</title>
        <description>&lt;p&gt;最近在看一个基于 TanStack 的项目时，我遇到了一个很奇怪的地方。&lt;/p&gt;

&lt;p&gt;目录明明是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apps/tanstack-app/src/routes/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;按照我过去的经验，我下意识会把这里理解成“前端”。&lt;/p&gt;

&lt;p&gt;结果继续往下看，却出现了：&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@libs/database/schema/order&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后直接：&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lastMonthOrders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我当时第一个反应是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;TanStack 不是前端吗？怎么直接查数据库了？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这个疑问对我来说其实挺有意思。&lt;/p&gt;

&lt;p&gt;因为我的第一份工作，接触的正是 Node.js、Express、Koa，以及那个时代非常典型的&lt;strong&gt;前后端分离架构&lt;/strong&gt;。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;一我的第一份工作从-express-换到-koa&quot;&gt;一、我的第一份工作：从 Express 换到 Koa&lt;/h2&gt;

&lt;p&gt;我第一份工作接触 Node.js 时，项目最开始使用的是 Express。&lt;/p&gt;

&lt;p&gt;那个时候对后端的理解非常直接：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Request
   │
   ▼
Express
   │
   ├── Middleware
   ├── Router
   ├── Controller
   └── Business Logic
          │
          ▼
       Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Express 很简单。&lt;/p&gt;

&lt;p&gt;一个接口可能就是：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/api/orders&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;orders&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getOrders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;orders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于刚接触 Node.js 的我来说，这种模型很好理解：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;请求进来
  ↓
处理业务
  ↓
查数据库
  ↓
返回 JSON
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但随着业务量上来，我们开始发现 Express 已经无法很好地满足当时项目的需求。&lt;/p&gt;

&lt;p&gt;团队中的工程师们最终项目选择迁移到 Koa。&lt;/p&gt;

&lt;p&gt;我当时对这件事情最直观的理解其实很简单：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;我们需要一个更轻、更现代，也更适合当时 Node 异步模型的框架。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Koa 的 middleware 洋葱模型尤其漂亮：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;after&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;请求经过：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                Request
                   │
                   ▼
          ┌── Middleware A ──┐
          │                  │
          │  Middleware B    │
          │       │          │
          │       ▼          │
          │     Router       │
          │       │          │
          │       ▼          │
          │  Middleware B    │
          │                  │
          └── Middleware A ──┘
                   │
                   ▼
                Response
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当然，&lt;strong&gt;Express 换 Koa 本身并不会神奇地解决所有性能问题&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;真正决定系统吞吐量的还有业务逻辑、数据库、I/O、缓存、Node 版本、部署架构等很多东西。&lt;/p&gt;

&lt;p&gt;但在当时那个项目里，Koa 确实成为我们重新组织 Node 服务的重要一步。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;二那个年代我们坚信一件事情前后端必须分离&quot;&gt;二、那个年代我们坚信一件事情：前后端必须分离&lt;/h1&gt;

&lt;p&gt;我们的整体架构是典型的：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                 Internet
                    │
                    ▼
               ┌─────────┐
               │  Nginx  │
               │   / LB  │
               └────┬────┘
                    │
        ┌───────────┼───────────┐
        │           │           │
        ▼           ▼           ▼
      Node        Node        Node
      Koa         Koa         Koa
        │           │           │
        └───────────┼───────────┘
                    │
                    ▼
              Database / Cache
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;前端是前端。&lt;/p&gt;

&lt;p&gt;后端是后端。&lt;/p&gt;

&lt;p&gt;双方通过 HTTP API 通信：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;React / Vue
      │
      │ HTTP + JSON
      ▼
     API
      │
      ▼
     Koa
      │
      ▼
Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果后端扛不住怎么办？&lt;/p&gt;

&lt;p&gt;答案也非常符合那个年代互联网架构的直觉：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;横向扩容。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;一台 Node 不够：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那就两台：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Node
Node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还不够？&lt;/p&gt;

&lt;p&gt;继续加：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Node
Node
Node
Node
Node
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;理论上，只要应用服务保持尽量无状态，就可以不断复制 Node 实例。&lt;/p&gt;

&lt;p&gt;然后在前面放一层负载均衡：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                 User
                   │
                   ▼
            Load Balancer
             / Nginx
                   │
       ┌───────────┼───────────┐
       ▼           ▼           ▼
    Node-01     Node-02     Node-03
       │           │           │
       └───────────┼───────────┘
                   │
                   ▼
            Redis / Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;流量继续增加：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;3 Nodes
   ↓
10 Nodes
   ↓
30 Nodes
   ↓
100 Nodes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;甚至配合监控指标做动态扩容：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Traffic ↑
   │
CPU / Load ↑
   │
   ▼
Auto Scaling
   │
   ├── Node
   ├── Node
   ├── Node
   └── Node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;流量下来，再缩回去。&lt;/p&gt;

&lt;p&gt;现在看，这是很标准的 Cloud Native 思想。&lt;/p&gt;

&lt;p&gt;但那个时候我们未必会用今天这么多术语描述它。&lt;/p&gt;

&lt;p&gt;关心的是一件很朴素的事情：&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=&quot;三为什么-node-特别适合这种玩法&quot;&gt;三、为什么 Node 特别适合这种玩法？&lt;/h1&gt;

&lt;p&gt;这其实也是当年 Node.js 非常吸引人的地方。&lt;/p&gt;

&lt;p&gt;Node 的服务可以做得很轻。&lt;/p&gt;

&lt;p&gt;应用实例本身尽可能不保存状态：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Node Instance
     │
     ├── 接收请求
     ├── 执行业务
     ├── 查 Redis
     ├── 查数据库
     └── 返回 JSON
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;用户 Session 不要死死存在某个 Node 进程里。&lt;/p&gt;

&lt;p&gt;文件不要只放本机。&lt;/p&gt;

&lt;p&gt;关键状态交给：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Redis
Database
Object Storage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是 Node 本身就变成一个相对容易复制的计算单元：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;          ┌── Node
          ├── Node
Request ──┼── Node
          ├── Node
          ├── Node
          └── Node
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;哪台挂了？&lt;/p&gt;

&lt;p&gt;摘掉。&lt;/p&gt;

&lt;p&gt;流量大了？&lt;/p&gt;

&lt;p&gt;加机器。&lt;/p&gt;

&lt;p&gt;流量小了？&lt;/p&gt;

&lt;p&gt;减少实例。&lt;/p&gt;

&lt;p&gt;这种架构给我留下了很深的印象。&lt;/p&gt;

&lt;p&gt;因为它让我形成了一个持续很多年的 Web 架构直觉：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Frontend
     │
     │ API
     ▼
Backend Cluster
     │
     ▼
Data Layer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;三个世界应该明确分开。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;四前后端分离当时并不是潮流而是真的解决问题&quot;&gt;四、前后端分离当时并不是“潮流”，而是真的解决问题&lt;/h1&gt;

&lt;p&gt;今天很多人看到传统项目：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Controller
Service
DAO
DTO
REST API
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;会觉得繁琐。&lt;/p&gt;

&lt;p&gt;但如果经历过那个阶段，就会知道它为什么会流行。&lt;/p&gt;

&lt;p&gt;因为它解决的是非常现实的问题。&lt;/p&gt;

&lt;p&gt;前端可以独立开发：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Web
iOS
Android
小程序
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它们全部消费同一套 API：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;             Web
              │
             iOS
              │
Android ─── REST API ─── Backend
              │
           小程序
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后端也可以独立扩容：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Frontend
    │
    ▼
Gateway / LB
    │
    ├── Backend
    ├── Backend
    ├── Backend
    └── Backend
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;甚至组织架构也可以跟着拆：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Frontend Team
      │
      │ API Contract
      ▼
Backend Team
      │
      ▼
Infrastructure Team
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这套架构真正厉害的地方不是“代码漂亮”。&lt;/p&gt;

&lt;p&gt;而是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;每一层都可以独立变化。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;五但后来发现不是所有系统都需要这么重&quot;&gt;五、但后来发现：不是所有系统都需要这么重&lt;/h1&gt;

&lt;p&gt;问题出现在接触到更多内部业务/SaaS/数据密集型业务越来越多以后。&lt;/p&gt;

&lt;p&gt;比如今天只想在后台 Dashboard 显示一个数字：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;最近一个月订单数量。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;数据库其实只需要：&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orders&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;created_at&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但是按照经典前后端分离思路，整个调用链可能变成：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;React Component
       │
       ▼
React Query
       │
       ▼
HTTP Client
       │
       ▼
Nginx
       │
       ▼
API Router
       │
       ▼
Controller
       │
       ▼
Service
       │
       ▼
Repository
       │
       ▼
ORM
       │
       ▼
PostgreSQL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;返回的时候再原路走回来。&lt;/p&gt;

&lt;p&gt;对于复杂系统，这是合理的。&lt;/p&gt;

&lt;p&gt;但如果只是一个几个人的小团队，在做：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SaaS
AI 产品
后台管理系统
内部工具
创业 MVP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;就会开始产生另一个问题：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;我到底是在实现业务，还是在搬运 JSON？&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我的一位创业公司朋友，要得很明确：开展业务。不仅仅是前后端分离，还要考虑到人工成本。技术复杂度，要取舍。
说实话当时没太理解，后知后觉，技术终究还是要落地导向。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;六与此同时react-自己也遇到了问题&quot;&gt;六、与此同时，React 自己也遇到了问题&lt;/h1&gt;

&lt;p&gt;React 最初非常擅长：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;State
  │
  ▼
 UI
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但后来大家发现，Web App 大部分重要数据其实根本不属于浏览器。&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;订单
用户
商品
支付
通知
评论
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这些东西属于 Server。&lt;/p&gt;

&lt;p&gt;于是代码里出现大量：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/api/orders&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setOrders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后很快开始处理：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;loading
error
retry
cache
stale
refetch
dedupe
pagination
mutation
invalidation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;大家逐渐意识到：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sidebarOpen = true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;和：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;orders = getOrders()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;根本不是一种状态。&lt;/p&gt;

&lt;p&gt;前者是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Client State
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后者是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Server State
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;React Query 正是在这里爆发。&lt;/p&gt;

&lt;p&gt;它开始把：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fetch
cache
retry
refetch
stale
invalidation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;统一到：&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;useQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;useMutation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;里面。&lt;/p&gt;

&lt;p&gt;这也是后来 TanStack 故事真正开始的地方。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;七然后-router-也开始进入类型系统&quot;&gt;七、然后 Router 也开始进入类型系统&lt;/h1&gt;

&lt;p&gt;随着 TypeScript 普及，又出现了另一个问题。&lt;/p&gt;

&lt;p&gt;比如：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/users/123/orders?page=2&amp;amp;status=paid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实际上这里包含很多应用状态：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;123          → userId
page=2       → pagination
status=paid  → filter
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但传统 Router 很长时间本质还是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;URL = string
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是：&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;navigate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/users/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;TypeScript 根本不知道：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Route 存不存在
参数对不对
Search Params 合不合法
Loader 返回什么
Component 最终拿到什么
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;TanStack Router 做的事情，就是把：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Route
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;也拉进类型系统：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Route Tree
    │
    ├── Params
    ├── Search
    ├── Loader
    ├── Context
    └── Component
          │
          ▼
       TypeScript
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;到这里，事情已经开始发生变化了。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;八大人时代变了tanstack-start&quot;&gt;八、大人，时代变了，TanStack Start&lt;/h1&gt;

&lt;p&gt;现在看到的代码已经变成：&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@libs/database/schema/order&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                TanStack Start
       ┌───────────────────────────┐
       │                           │
Browser│ React                     │
       │ TanStack Router           │
       │ TanStack Query            │
       │                           │
───────┼──── Server Boundary ──────│
       │                           │
Server │ Server Function           │
       │ Business Logic            │
       │ Drizzle                   │
       │                           │
       └─────────────┬─────────────┘
                     │
                     ▼
                 PostgreSQL
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;前后端边界并没有消失。&lt;/p&gt;

&lt;p&gt;变化的是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;以前边界由两个项目 + HTTP API 表达，现在可以由 Framework + Bundler + Runtime 表达。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这对我来说是 TanStack 最有意思的地方。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;九于是我突然发现web-架构似乎绕了一圈&quot;&gt;九、于是我突然发现：Web 架构似乎绕了一圈&lt;/h1&gt;

&lt;p&gt;我第一份工作里的世界是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                 Nginx
                   │
       ┌───────────┼───────────┐
       ▼           ▼           ▼
      Koa         Koa         Koa
       │           │           │
       └───────────┼───────────┘
                   │
                   ▼
               Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;整个公司围绕着这样一个灵活先进的架构展开工作。
我们不断强调：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Frontend ≠ Backend
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;今天 TanStack Start 的世界却越来越像：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;             Full-stack App
        ┌─────────────────────┐
        │ React               │
        │ Router              │
        │ Query               │
        │─────────────────────│
        │ Server Function     │
        │ ORM                 │
        └──────────┬──────────┘
                   │
                   ▼
               Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;hr /&gt;

&lt;h1 id=&quot;十第一次放在一起和今天放在一起完全不同&quot;&gt;十、第一次“放在一起”和今天“放在一起”完全不同&lt;/h1&gt;

&lt;p&gt;早期 Web：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Server
 ├── Template
 ├── Business Logic
 └── Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;很多时候是因为技术能力有限。&lt;/p&gt;

&lt;p&gt;后来前后端分离：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Frontend
    │
    │ REST API
    ▼
Backend
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解决了：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;团队解耦
多客户端
独立部署
独立扩容
API 标准化
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;今天 Full-stack TypeScript 又开始把代码整合：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;React
Router
Query
────────────
Server Function
ORM
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;是因为我们已经有：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;TypeScript
Bundler
Vite
SSR
Server Functions
Type-safe ORM
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;能够在：&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;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;运行时仍然分离。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所以真正的变化不是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;分离 → 不分离
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;而更像：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;物理分离
   │
   ▼
逻辑分离
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;十一那我第一份工作那套-node-横向扩容过时了吗&quot;&gt;十一、那我第一份工作那套 Node 横向扩容过时了吗？&lt;/h1&gt;

&lt;p&gt;完全没有。&lt;/p&gt;

&lt;p&gt;这是我认为讨论 TanStack 时特别容易忽略的一点。&lt;/p&gt;

&lt;p&gt;TanStack Start 改变的是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Application Programming Model
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它没有改变服务器的物理规律。&lt;/p&gt;

&lt;p&gt;如果今天一个 TanStack Start 服务流量越来越大，一样可以：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                    LB
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
   TanStack      TanStack     TanStack
    Node-01       Node-02      Node-03
        │           │           │
        └───────────┼───────────┘
                    │
                    ▼
              Redis / DB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;继续增长：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;3 instances
     ↓
10 instances
     ↓
50 instances
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;今天可能不再是我当年手工配置服务器。&lt;/p&gt;

&lt;p&gt;而变成：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Docker
Kubernetes
ECS
Cloud Run
Serverless
Edge Runtime
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但核心思想没有变：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;应用层尽量无状态，然后横向复制计算节点。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;所以 Express、Koa 那个时代积累下来的架构思想没有消失。&lt;/p&gt;

&lt;p&gt;只是 Infrastructure 抽象得越来越高。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;十二最后再谈一个问题tanstack-性能真的更好吗&quot;&gt;十二、最后再谈一个问题：TanStack 性能真的更好吗？&lt;/h1&gt;

&lt;p&gt;第一次看到：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Component
   ↓
Server Function
   ↓
Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;很容易产生一种感觉：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;少了 REST、Controller、Service，所以一定更快。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;其实不一定。&lt;/p&gt;

&lt;p&gt;因为 Server Function 并没有消灭网络。&lt;/p&gt;

&lt;p&gt;浏览器访问服务器仍然要经历：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Network
   ↓
Serialization
   ↓
Authentication
   ↓
Server Execution
   ↓
Database
   ↓
Serialization
   ↓
Network
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以前写：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST /api/orders
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;现在写：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;createOrder()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;开发体验可能完全不同。&lt;/p&gt;

&lt;p&gt;但物理世界并没有改变。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;十三tanstack-真正可能改善的是数据什么时候加载&quot;&gt;十三、TanStack 真正可能改善的，是“数据什么时候加载”&lt;/h1&gt;

&lt;p&gt;传统 SPA 很容易出现：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTML
 ↓
JavaScript
 ↓
React
 ↓
Router
 ↓
Component Mount
 ↓
fetch()
 ↓
API
 ↓
Database
 ↓
Render
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这会产生典型 Waterfall：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;JS
────────&amp;gt;

          API A
          ────────&amp;gt;

                    API B
                    ────────&amp;gt;

                              Render
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;TanStack Router + Query + SSR 的一个价值，就是更早知道：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;这个 Route 到底需要什么数据？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是可以：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;             ┌── Query A ─────────┐
Route ───────┼── Query B ─────────┼── Render
             └── Query C ─────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;能并行就并行。&lt;/p&gt;

&lt;p&gt;再结合：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Prefetch
Cache
SSR
Streaming
staleTime
Route-level Code Splitting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;最终改善的是用户感知性能。&lt;/p&gt;

&lt;p&gt;这和：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Koa 比 Express 每秒多处理多少请求&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;已经是两个完全不同层面的性能问题了。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;十四而-tanstack-同样可以被写得很慢&quot;&gt;十四、而 TanStack 同样可以被写得很慢&lt;/h1&gt;

&lt;p&gt;比如：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Route
 ↓
Loader
 ↓
Server Function
 ↓
Component
 ↓
Query
 ↓
Server Function
 ↓
Child Component
 ↓
Query
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一样可以制造严重的 Request Waterfall。&lt;/p&gt;

&lt;p&gt;或者：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SSR 请求一次
     ↓
Hydration
     ↓
客户端又请求一次
     ↓
Mutation
     ↓
invalidateQueries(&apos;*&apos;)
     ↓
全部重新请求
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;于是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;一次页面访问
      │
      ▼
十几个请求
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;再先进的 Framework 也救不了错误的数据模型。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;十五所以今天看性能我已经不会只看-framework-benchmark&quot;&gt;十五、所以今天看性能，我已经不会只看 Framework Benchmark&lt;/h1&gt;

&lt;p&gt;第一份工作时，我可能更关心：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Express
   VS
Koa

QPS 谁高？
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;今天再看一个 TanStack 项目，我反而会看：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                    Performance
                         │
        ┌────────────────┼────────────────┐
        │                │                │
        ▼                ▼                ▼
      Server           Browser           Data
        │                │                │
      TTFB             Bundle          Waterfall
      CPU              Hydration       Cache Hit
      Memory           LCP             DB Query
      DB Pool          FCP             Duplicate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;尤其是：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;TTFB
Bundle Size
Hydration Cost
LCP
Request Waterfall
Cache Hit Rate
Database Query Time
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这些指标放在一起，才是真正的 Web 性能。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;十六从我的第一份工作到今天&quot;&gt;十六、从我的第一份工作到今天&lt;/h1&gt;

&lt;p&gt;回头看，这条路线非常有意思：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;我的第一份工作
      │
      ▼
   Express
      │
      │ 业务需求增长
      ▼
     Koa
      │
      ▼
前后端分离
      │
      ▼
Nginx / Load Balancer
      │
      ▼
Node 横向复制
      │
      ▼
动态扩缩容
      │
      │
      │ 这些年 Web 继续发展
      ▼
React / SPA
      │
      ▼
React Query
      │
      ▼
TanStack Query
      │
      ▼
TanStack Router
      │
      ▼
TanStack Start
      │
      ▼
Full-stack TypeScript
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当年我学习的是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;怎么把前端和后端拆得足够干净。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;今天 TanStack 给我的感觉却是：&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;p&gt;恰恰相反。&lt;/p&gt;

&lt;p&gt;如果没有前后端分离、无状态服务、负载均衡、横向扩容这些思想，就不会有今天的大规模 Web 系统。&lt;/p&gt;

&lt;p&gt;TanStack 做的不是推翻它们。&lt;/p&gt;

&lt;p&gt;而是在另一个维度重新划分边界。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;结语&quot;&gt;结语&lt;/h1&gt;

&lt;p&gt;从 Express 到 Koa，再到今天的 TanStack，我最大的感受其实不是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;哪个 Framework 更先进？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而是 Web 工程一直在做同一件事情：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;移动复杂度。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Express/Koa 时代，我们把复杂度拆到：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Frontend
API
Backend
Infrastructure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过 Nginx、负载均衡和大量无状态 Node 实例解决扩展问题：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                    Traffic
                       │
                       ▼
                  Nginx / LB
                       │
           ┌───────────┼───────────┐
           ▼           ▼           ▼
         Node        Node        Node
           │           │           │
           └───────────┼───────────┘
                       ▼
                    Data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;今天 TanStack 又尝试把 Application Layer 重新拉近：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;             TypeScript
                 │
       ┌─────────┴─────────┐
       │                   │
     Client              Server
       │                   │
     React          Server Function
       │                   │
     Router               ORM
       │                   │
     Query              Database
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但底层那些老问题一个都没有消失：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;网络延迟
并发
数据库瓶颈
缓存
无状态
负载均衡
横向扩容
故障恢复
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以十几年以后再看 TanStack，我反而更能理解它为什么会出现。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;它不是证明“前后端分离错了”。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;它真正提出的问题是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;当 TypeScript、Bundler、SSR、Server Function 和 ORM 都已经成熟以后，我们还有必要为了保持边界，而支付过去那么高的工程成本吗？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;答案最终不会由 Framework 的宣传页决定。&lt;/p&gt;

&lt;p&gt;而会由真实项目回答：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;开发更快了吗？
       │
系统更简单了吗？
       │
性能更好了吗？
       │
扩容还容易吗？
       │
出了问题还能定位吗？
       │
团队变大以后还能维护吗？
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果这些问题都能得到好的答案，那么 TanStack 代表的 Full-stack TypeScript 才真正完成了一次架构进化。&lt;/p&gt;

&lt;p&gt;否则，我们只是把当年清清楚楚写在：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Nginx
API
Controller
Service
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;里的复杂度，&lt;/p&gt;

&lt;p&gt;藏进了一个更加现代、更加漂亮的框架里。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;技术很少真正消灭复杂度。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;它更多时候，只是在决定我们下一次会在哪里遇见复杂度。&lt;/strong&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 28 Sep 2026 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/talk-about-tanstack.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/talk-about-tanstack.html</guid>
        
        
      </item>
    
      <item>
        <title>Python 执行卡了1小时，最终我杀掉一个 TCP 连接</title>
        <description>&lt;p&gt;今天遇到一个很有意思的故障。&lt;/p&gt;

&lt;p&gt;服务器上有个LLM Call程序，负责批量处理数据，中间会并发调用中转站 LLM。
跑着跑着，它突然不动了。&lt;/p&gt;

&lt;p&gt;没有报错，没有退出，服务也没挂。&lt;/p&gt;

&lt;p&gt;进程还好端端地活着：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root  471728  0.1  8.4 6316968 627664 ?  Sl  12:31  0:05 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
/root/project/llmcall/.venv/bin/python3 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
app/services/gate_service.py &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt; Bing
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这种问题其实比直接报错更讨厌。&lt;/p&gt;

&lt;p&gt;于是我决定一层层往下找。&lt;/p&gt;

&lt;h2 id=&quot;它到底在干什么&quot;&gt;它到底在干什么？&lt;/h2&gt;

&lt;p&gt;第一眼看 CPU。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pidstat &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 471728 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;连续十几秒：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;%usr  %system  %wait  %CPU
0.00     0.00   0.00  0.00
0.00     0.00   0.00  0.00
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;完全不动。&lt;/p&gt;

&lt;p&gt;至少可以排除一个方向：它不是陷入死循环。&lt;/p&gt;

&lt;p&gt;再看进程状态：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /proc/471728/status | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;State|Threads|VmRSS|VmSize&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;State:   S (sleeping)
VmSize:  6316968 kB
VmRSS:    627664 kB
Threads:       24
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;24 个线程，进程正在睡觉。&lt;/p&gt;

&lt;p&gt;那它在等什么？&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /proc/471728/wchan
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Linux 回答：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;futex_do_wait
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这就有意思了。&lt;/p&gt;

&lt;p&gt;主线程正在等某个线程同步条件。可能是锁，可能是 Condition，也可能是 Future。&lt;/p&gt;

&lt;p&gt;但这还不能叫死锁。&lt;/p&gt;

&lt;p&gt;因为我的程序用了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ThreadPoolExecutor&lt;/code&gt;，主线程等待 worker，本来就是正常行为。&lt;/p&gt;

&lt;p&gt;真正的问题是：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;它在等谁？&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;24-个线程里只有一个罪魁祸首&quot;&gt;24 个线程里，只有一个罪魁祸首&lt;/h2&gt;

&lt;p&gt;这时候 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;py-spy&lt;/code&gt; 非常好用：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;uv run py-spy dump &lt;span class=&quot;nt&quot;&gt;--pid&lt;/span&gt; 471728
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;主线程的调用栈很快给出了第一个答案：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MainThread
    wait
    wait
    as_completed
    gate_on_batch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;原来主线程停在：&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;as_completed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它正在等线程池里的任务全部完成。&lt;/p&gt;

&lt;p&gt;继续往下看。&lt;/p&gt;

&lt;p&gt;线程池大约有 20 个 worker，其中 19 个都已经变成：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ThreadPoolExecutor-10_x
    _worker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;idle。&lt;/p&gt;

&lt;p&gt;只有一个线程还在工作：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ThreadPoolExecutor-10_15
    read (ssl.py)
    recv (ssl.py)
    read (httpcore)
    _receive_response_headers
    httpx
    openai
    ChatOpenAI
    invoke
    chat (client.py:67)
    run_two_stage_gate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;看到这里，事情已经非常清楚了。&lt;/p&gt;

&lt;p&gt;不是 Python 死锁。&lt;/p&gt;

&lt;p&gt;不是 CPU 卡住。&lt;/p&gt;

&lt;p&gt;也不是数据库。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;整个程序都在等一次 LLM 请求的 HTTP Response Header。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;把现场简化一下，大概是这样：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MainThread
    │
    └── as_completed()
            │
            ├── Worker 0   ✓
            ├── Worker 1   ✓
            ├── ...
            ├── Worker 14  ✓
            │
            ├── Worker 15
            │      │
            │      └── ChatOpenAI.invoke()
            │              ↓
            │            HTTPX
            │              ↓
            │          SSL recv()
            │              ↓
            │        等服务器响应……
            │
            └── Worker 16~19 ✓
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;19 个人都下班了。&lt;/p&gt;

&lt;p&gt;最后一个人坐在工位上等电话。&lt;/p&gt;

&lt;p&gt;而老板 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;as_completed()&lt;/code&gt; 坚持：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;等他回来我们才能走。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是整个 batch 就这么耗着。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;原来是超时设置没生效&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;回头检查 LLM client：&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_llm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ChatOpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;没有显式设置 timeout。&lt;/p&gt;

&lt;p&gt;外层又恰好在等待全部 Future：&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;future&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;as_completed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;两个设计碰到一起，就形成了一个很经典的故障：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;一个 HTTP 请求长时间不返回
            ↓
一个 Future 永远不完成
            ↓
as_completed() 一直等
            ↓
整个 Batch 一直不结束
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从服务器外面看，则是一个非常迷惑的状态：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Python 活着
CPU = 0%
没有异常
没有退出
也没有进展
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;程序不是挂了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;程序只是愿意一直等。&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;但我不想杀-python&quot;&gt;但我不想杀 Python&lt;/h2&gt;

&lt;p&gt;定位清楚之后，还有一个现实问题。&lt;/p&gt;

&lt;p&gt;这个任务已经跑了很久，前面大量数据都处理完了。&lt;/p&gt;

&lt;p&gt;最简单的处理当然是：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-9&lt;/span&gt; 471728
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但这相当于为了一个人没下班，把整栋楼的电闸拉了。&lt;/p&gt;

&lt;p&gt;我真正想做的是：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;能不能只让那个卡住的 HTTP 请求失败？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果 socket 被强制断开，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssl.recv()&lt;/code&gt; 就应该返回异常。&lt;/p&gt;

&lt;p&gt;异常会一路冒泡到 HTTPX、OpenAI SDK 和 LangChain。SDK 可能重试；即使不重试，至少这个 Future 会结束，而不是无限等待。&lt;/p&gt;

&lt;p&gt;于是我看了一眼连接：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ss &lt;span class=&quot;nt&quot;&gt;-ntp&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;pid=471728&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;ESTAB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;除了 PostgreSQL，只剩一条对外 HTTPS：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;172.31.77.XXX:41502 -&amp;gt; XXX.18.3.115:443
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;大概率就是它。&lt;/p&gt;

&lt;p&gt;于是我没有杀进程，也没有尝试杀 Python 线程。&lt;/p&gt;

&lt;p&gt;我杀了这条 TCP 连接：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ss &lt;span class=&quot;nt&quot;&gt;-K&lt;/span&gt; dst XXX.18.3.115 dport &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 443 sport &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 41502
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后发生了这次故障里最有意思的一幕。&lt;/p&gt;

&lt;h2 id=&quot;程序活了&quot;&gt;程序活了&lt;/h2&gt;

&lt;p&gt;再看网络连接。&lt;/p&gt;

&lt;p&gt;原来的：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;41502 -&amp;gt; XXX.18.3.115:443
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;消失了。&lt;/p&gt;

&lt;p&gt;紧接着出现了一批新的 HTTPS 连接：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;55160 -&amp;gt; xxx.18.2.115:443
55164 -&amp;gt; xxx.18.2.115:443
55170 -&amp;gt; xxx.18.2.115:443
...
51480 -&amp;gt; xxx.18.3.115:443
51544 -&amp;gt; xxx.18.3.115:443
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;程序重新开始跑了。&lt;/p&gt;

&lt;p&gt;大概发生了这样一件事：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;卡住的 socket
      ↓
ss -K 强制断开
      ↓
ssl.read() 返回异常
      ↓
HTTP client / SDK 接收到失败
      ↓
retry / 当前 Future 结束
      ↓
as_completed() 终于可以继续
      ↓
下一批任务启动
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 24 Sep 2026 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/py-spy.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/py-spy.html</guid>
        
        
      </item>
    
      <item>
        <title>Hermes Agent 飞书机器人：私聊正常，群聊不响应的真正原因</title>
        <description>&lt;p&gt;最近接入 &lt;a href=&quot;https://github.com/NousResearch/hermes-agent&quot;&gt;Hermes Agent&lt;/a&gt; 的飞书机器人时，遇到一个非常迷惑的问题：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;私聊机器人：正常&lt;/li&gt;
  &lt;li&gt;群里 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@bot hi&lt;/code&gt;：完全没反应&lt;/li&gt;
  &lt;li&gt;Hermes 日志里甚至没有 group message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;第一反应通常会怀疑：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;飞书权限没开&lt;/li&gt;
  &lt;li&gt;事件订阅有问题&lt;/li&gt;
  &lt;li&gt;WebSocket 没连上&lt;/li&gt;
  &lt;li&gt;机器人没加群&lt;/li&gt;
  &lt;li&gt;Hermes Feishu adapter 有 bug&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;结果都不是。&lt;/p&gt;

&lt;p&gt;真正的问题只有一行配置：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-env&quot;&gt;FEISHU_GROUP_POLICY=open
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;问题现象&quot;&gt;问题现象&lt;/h1&gt;

&lt;p&gt;飞书后台权限全部正常：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;im.message.receive_v1&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;获取群组中用户 @ 机器人消息&lt;/li&gt;
  &lt;li&gt;获取群组中所有消息&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;私聊能正常触发：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Inbound dm message received
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;但群里：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@bot hi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;完全没任何日志。&lt;/p&gt;

&lt;p&gt;这个现象特别容易误导人。&lt;/p&gt;

&lt;p&gt;因为看起来像：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;飞书根本没把 group event 推送给 Hermes。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;实际上并不是。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;真正原因&quot;&gt;真正原因&lt;/h1&gt;

&lt;p&gt;Hermes Feishu gateway 默认配置是：&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;group_policy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;FEISHU_GROUP_POLICY&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;allowlist&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;默认值：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-env&quot;&gt;FEISHU_GROUP_POLICY=allowlist
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;也就是说：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hermes 默认不会自动处理所有群消息。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;allowlist&lt;/code&gt; 模式下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;群不在白名单&lt;/li&gt;
  &lt;li&gt;用户不在 allowlist&lt;/li&gt;
  &lt;li&gt;mention gating 不满足&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;消息会被直接过滤。&lt;/p&gt;

&lt;p&gt;于是你看到的现象就是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;私聊正常&lt;/li&gt;
  &lt;li&gt;群聊完全没反应&lt;/li&gt;
  &lt;li&gt;日志里甚至没有 raw message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;非常像“飞书事件没推过来”。&lt;/p&gt;

&lt;p&gt;但其实：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;是 Hermes 自己静默 drop 了 group event。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;解决方案&quot;&gt;解决方案&lt;/h1&gt;

&lt;p&gt;在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; 里增加：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-env&quot;&gt;FEISHU_GROUP_POLICY=open
FEISHU_ALLOW_BOTS=all
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后重启：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hermes restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;立刻恢复正常。&lt;/p&gt;

&lt;p&gt;群里：&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@bot hi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;马上能收到回复。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;为什么这个问题很坑&quot;&gt;为什么这个问题很坑&lt;/h1&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;WebSocket&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;pre&gt;&lt;code class=&quot;language-env&quot;&gt;FEISHU_GROUP_POLICY
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;默认值太“保守”。&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;一个经验&quot;&gt;一个经验&lt;/h1&gt;

&lt;p&gt;如果你遇到：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;飞书私聊正常&lt;/li&gt;
  &lt;li&gt;群 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@bot&lt;/code&gt; 完全无响应&lt;/li&gt;
  &lt;li&gt;Hermes 没 group raw log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;先检查：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-env&quot;&gt;FEISHU_GROUP_POLICY
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;别先折腾飞书后台。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;相关 issue：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/NousResearch/hermes-agent/issues/6889?utm_source=chatgpt.com&quot;&gt;Hermes Issue #6889&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 12 May 2026 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/hermes-with-lark.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/hermes-with-lark.html</guid>
        
        
      </item>
    
      <item>
        <title>用3年修复996工作后应激，用AI重构自己</title>
        <description>&lt;p&gt;时间总是以意想不到的方式流逝。3年前，因为身体原因结束了一段996工作后，我进入了一段极度迷茫的自我探索期。我一直以为，离开996就结束了。后来才发现，结束的只是工作时间，而不是那种状态。&lt;/p&gt;

&lt;p&gt;真正留下来的，是一种很难描述的东西。对时间的焦虑，对效率的执念，对“必须马上有产出”的恐惧。哪怕在休息的时候，也会下意识打开电脑；哪怕没有任务，也会制造任务给自己。这种状态不像累，更像一种长期被训练出来的条件反射。&lt;/p&gt;

&lt;p&gt;它不会因为你辞职就消失，它会跟着你，进入你的下一段生活。焦虑，焦虑，焦虑，怀疑，怀疑，怀疑。莫名其妙的疲劳感。我试图用过各种方式来对抗它，药物，旅行，睡眠，但效果都很有限。&lt;/p&gt;

&lt;p&gt;最可恶的是，我隐隐约约知道为什么自己状态这么差，这才是最难修复的部分。因为问题不在于你有没有在休息，而在于你是否还在用原来的逻辑看待世界。&lt;/p&gt;

&lt;p&gt;人生观，或者是人生的体感就已经被996式的工作异化了。&lt;/p&gt;

&lt;p&gt;值得庆幸的是，从一开始我便是一个AI重度用户，一开始，我只是把像 ChatGPT 和 Claude 这样的工具，当作效率助手。写代码、查资料、整理信息，它们确实让我更快完成事情。但很快我发现，如果只是把AI当成更快的工具，那我只是把996的效率逻辑放大了而已。&lt;/p&gt;

&lt;p&gt;我用大模型去不断分析自己的状态。用大模型去认知自己，让大模型参与“决策”和“结构”的部分，而不仅仅是用于“劳作”。&lt;/p&gt;

&lt;p&gt;从某种意义上说，AI帮我“重新分配了注意力”。996留下的问题，不只是身体透支，而是注意力被长期错误分配。你习惯把精力放在低价值、高重复的任务上，因为那是被要求的，而不是被选择的。&lt;/p&gt;

&lt;p&gt;现在每一天AI的知识如潮水般袭来，我们可能会感同身受，近些日子AI的知识是在被动灌入的。
谁都知道一点，好像谁都能玩好。
可是，可惜的是，明明知道该去看看langchain该去看看skill，可就是提不起精神来去学一点。&lt;/p&gt;

&lt;p&gt;我是一个研发者，却成了AI革命进行时的场外人。&lt;/p&gt;

&lt;p&gt;AI浪潮带来的焦虑，也让空洞的追求成为常态。我常会被提问“AI是不是可以XXX”。大多数非技术关注者，更先开始“AI幻觉”，他们大概仅仅只是刷了下抖音，便以为在追赶时代。可是我太疲劳，都没有留意这些时代弄潮儿产物。对什么OpenClaw养龙虾，对什么harness engineering提不起一点兴趣。&lt;/p&gt;

&lt;p&gt;可以明确的留意到，初级岗位在消失，脑力型劳动均受到了AI的“替（ying）代（xiang）”。当前这个阶段消失的不是职业，而是任务。初级分析师花80%时间做的事情，正在被AI压缩到20分钟。复杂的技术工具，在被AI简化，在被抖音上的各种网红宣传，这些技术门槛，不是特别难突破。我们不得不思考，去重新定义”一个人的价值在哪里”。&lt;/p&gt;

&lt;p&gt;我不断追问ChatGPT，我的定位在哪里，我该怎么办，让他给我各种建议。可我就是没有任何行动。&lt;/p&gt;

&lt;p&gt;历次技术革命——蒸汽机、电力、互联网——最终都创造了比消灭更多的就业。但转型过程中，总有一代人付出了沉重代价。AI革命的速度远超以往，留给社会适应的时间窗口更短。政策制定者、教育体系、个人的反应速度，将决定这场转型是软着陆还是硬撕裂。&lt;/p&gt;

&lt;p&gt;目前我唯一能从这股浪潮中，感受到自己还能有点作用的地方是——尽量借用AI的思考作出生活上的判断。
毕竟生产力在加速，但组织、制度、教育都还没准备好。唯一值得请教的只有AI，所以我不断的用AI辅助思考，以求无漏。
聪明的人，聪明的观点太多了，我只能这样追赶。&lt;/p&gt;

&lt;p&gt;平庸的奋斗者，该如何走后面的路？&lt;/p&gt;

&lt;p&gt;于是我让AI做了下面的表格。这3年我问过AI很多问题，职业方向、人生规划、怎么从焦虑里出来。它每次都回答得很认真，给我列条目，画框架，分析处境，像一个永远不会不耐烦的人。我照着做的，大概不到两成。
我没有创业，没有想通，没有涅槃重生。我只是慢慢地，重新开始觉得一件普通的事情也可以做，不是因为它有用，只是因为我想做。这个感觉曾经消失了很久，我一度以为它不会回来。
有时候我也会想，也许AI真的会把所有事情都重写一遍，我现在做的这点什么，过几年看来什么都不算。也许吧。但那是以后的事。我现在只知道，我今天做了，比昨天多想了一点，这就够了。徒劳这件事，等真的徒劳了再说。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;时间阶段&lt;/th&gt;
      &lt;th&gt;技术状态&lt;/th&gt;
      &lt;th&gt;经济/工作变化&lt;/th&gt;
      &lt;th&gt;社会结构变化&lt;/th&gt;
      &lt;th&gt;个体层面的变化&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;2024–2026（已发生/正在发生）&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;大模型成熟（如 ChatGPT、Claude）&lt;/td&gt;
      &lt;td&gt;AI辅助工作成为标配，生产力提升&lt;/td&gt;
      &lt;td&gt;白领岗位开始“任务级压缩”，但结构未变&lt;/td&gt;
      &lt;td&gt;会用AI的人效率翻倍，不会用的人开始掉队&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;2026–2028（短期）&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;AI Agent兴起（能执行多步骤任务）&lt;/td&gt;
      &lt;td&gt;企业“少人高产”，中间层岗位收缩&lt;/td&gt;
      &lt;td&gt;出现结构性摩擦（岗位减少但新岗位未完全出现）&lt;/td&gt;
      &lt;td&gt;初级岗位压力最大，个体差距迅速拉开&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;2028–2030（过渡期）&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;AI深度嵌入业务流程&lt;/td&gt;
      &lt;td&gt;工作被拆解为模块，部分自动化&lt;/td&gt;
      &lt;td&gt;自由职业、项目制增加，公司边界变模糊&lt;/td&gt;
      &lt;td&gt;个体开始“AI增强”，但适应能力分化严重&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;2030–2032（结构变化期）&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;AI系统协作（Agent网络）&lt;/td&gt;
      &lt;td&gt;大量任务自动化，传统岗位减少&lt;/td&gt;
      &lt;td&gt;新型分层出现（AI驾驭者 / 协作者 / 被替代群体）&lt;/td&gt;
      &lt;td&gt;“稳定职业”概念弱化，路径变不确定&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;2032–2035（制度调整期）&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;AI成为基础设施（类似电力/互联网）&lt;/td&gt;
      &lt;td&gt;财富创造方式改变，生产力极度集中&lt;/td&gt;
      &lt;td&gt;政策介入（UBI探索、AI税、数据产权）&lt;/td&gt;
      &lt;td&gt;个体不再完全依赖“工作”获得收入&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;2035以后（长期）&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;高级AI/类AGI逐步成熟（不确定性高）&lt;/td&gt;
      &lt;td&gt;经济模式可能重构（人类劳动占比下降）&lt;/td&gt;
      &lt;td&gt;社会从“工作驱动”转向“资源分配驱动”&lt;/td&gt;
      &lt;td&gt;人类重新定义价值、意义和角色&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Mon, 04 May 2026 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/fuck996.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/fuck996.html</guid>
        
        
      </item>
    
      <item>
        <title>把玩crAPI</title>
        <description>&lt;p&gt;Github从来不缺好玩的，最近在研究API攻防，注意到了&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crAPI&lt;/code&gt;。全称是“​​完全脆弱的API​​”。正如其名，它是一个被故意设计得千疮百孔的API交互站。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/OWASP/crAPI&quot;&gt;crAPI&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd cd crAPI-main/deploy/docker
docker compose -f docker-compose.yml --compatibility up -d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;项目内设置的挑战之一是这样的：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Challenge 1 - Access details of another user’s vehicle
To solve the challenge, you need to leak sensitive information of another user’s vehicle.

Since vehicle IDs are not sequential numbers, but GUIDs, you need to find a way to expose the vehicle ID of another user.

Find an API endpoint that receives a vehicle ID and returns information about it.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;解法倒是蛮简单，可以通过观察发现不同API直接有关联性，进而找到泄露点：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Challenge 1 - Access details of another user’s vehicle

Detailed solution

1. Login to the application from http://localhost:8888/login
2. From the Dashboard, choose Add a Vehicle and add the vehicle by providing the VIN and pincode received in Mailhog mailbox after Signup or by reinitiating from Dashboard page.
After the vehicle details are verified successful, the vehicle will get added and then be populated in the Dashboard page.
4. Observe the request sent when we click Refresh Location. It can be seen that the endpoint is in the format /identity/api/v2/vehicle/&amp;lt;vehicleid&amp;gt;/location.
5. Sensitive information like latitude and longitude are provided back in the response for the endpoint. Send the request to Repeater for later purpose.
6. Click Community in the navbar to visit http://localhost:8888/forum
7. It can be observed that the forum posts are populated based on the response from /community/api/v2/community/posts/recent endpoint. On further analysing the response, it can be seen that vehicleid is also received back corresponding to the author of each post.
8. Edit the vehicleid in the request sent to Repeater in Step 5 with the vehicleid received from endpoint /community/api/v2/community/posts/recent.
9. Upon sending the request, sensitive details like latitude, longitude and full name are received in the response.

The above challenge was completed using Burp Suite Community Edition.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在折腾过程中，踩到的坑是：使用Docker启动，默认监听的地址是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt;，这个地址看起来是如此正常，却与&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Burp Suite&lt;/code&gt;工具水土不服，BS默认不截获来自localhost的流量。&lt;/p&gt;

&lt;p&gt;最终发现，最简单的办法是设置:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; sudo sh -c &apos;echo &quot;127.0.0.1 crapi.local&quot; &amp;gt;&amp;gt; /etc/hosts&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过访问伪域名&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crapi.local&lt;/code&gt;，便能截获API请求了。&lt;/p&gt;

&lt;p&gt;当然这些都不足够有趣，有趣的是我发现有一个印度Youtuber &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Medusa&lt;/code&gt;用crAPI录制了一系列教学视频。
嚯，第一次觉得印度姑娘有魅力。&lt;/p&gt;
</description>
        <pubDate>Wed, 29 Oct 2025 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/crapi.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/crapi.html</guid>
        
        
      </item>
    
      <item>
        <title>我所想体验的一切</title>
        <description>&lt;p&gt;我的脑子被物欲绑架了。回顾这种物欲纠结的感觉，大概从之前纠结要买哪一块表开始，从纠结哪一辆车开始。
这种折磨的表现是：可以从21点刷闲鱼直到凌晨1点。然后现在已忘记到底要买什么。
进一步反思，我发现自己总会被一些固定的念头绑架，我盘点了一下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;需要花时间的&lt;/strong&gt;：看 DHH 演讲（Rails作者近期神作）、玩 Omarchy（DHH订制的Archlinux）、国际站外贸上货（我已经拖延快1年了）、读书（囤了好多专题的书籍）、看大模型论文（看了个开头，浑浑噩噩）、自研大模型软件（做了一点点，但是没有驱动力了）。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;需要长期跟踪的&lt;/strong&gt;：Youtube订阅的一系列视频、《凡人修仙传》、《凸变英雄》。&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;：摩托车驾照、本田 CM300、开源软件认证、洗衣机、摔倒报警腕表。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;除了追更《凡人修仙传》。其它很有可能全死在 TODO 清单里，尸骨无存。&lt;/p&gt;

&lt;p&gt;心魔？？？&lt;/p&gt;

&lt;p&gt;以前会硬上一下，非得达到是目的，牺牲休息时间也可。
现在缺了这口劲儿。
想着如今已经年过三十，不妨与自己和解，试试别的路数。&lt;/p&gt;

&lt;p&gt;盘点下来，《凡人修仙传》实在是无法割舍，每周六准时享受。《凸变英雄》这种餐后甜点，也快追完了。这也仅仅占用周六2个小时。&lt;/p&gt;

&lt;p&gt;DHH演讲最多2个小时，不过是没时间看罢了，可能也就是某个空闲时，便能吸收其精华。其实已经知道个大概了，人家开发理念确实好。&lt;/p&gt;

&lt;p&gt;Omarchy Linux已经在我小机器装好了，没想到由于各种情况，基本耗费了1天时间，体验上了，还缺个梯子，未来想用于开发机。&lt;/p&gt;

&lt;p&gt;读书，我一直在囤，我总是在深夜开始想念他们。有一个专题的书籍，哪怕每天看5页呢。&lt;/p&gt;

&lt;p&gt;国际站外贸上货，我也不知道我在等啥，就是没心情去搞，这个投资了小五万，现在就缺个上货。在等契机，还没想好。&lt;/p&gt;

&lt;p&gt;看大模型论文，确实有启发，这周抽空看了一下，总是被细碎的生活琐事打断，但是沉浸式与GPT交互读论文，还是小有启发的。&lt;/p&gt;

&lt;p&gt;Youtube订阅的一系列视频，每当我打开机器，我总是随机在算法引导下游走，或许在做饭的时候听一下即可。&lt;/p&gt;

&lt;p&gt;调研数据中台，调研机器人，其实就是更商业沟通的事情，我倾向于在工作日处理。&lt;/p&gt;

&lt;p&gt;还有花小钱就能买到的物件，现在差一个白板，要贴满整面墙。还差一双防滑拖鞋，给老婆准备的，或许可以交给她解决？或许可以在刷抖音时候解决？也说不清在纠结啥，很占脑子。心疼这点钱，总还是是无效投入。&lt;/p&gt;

&lt;p&gt;驾照，11月才能增加摩托车，增加后还得算算能不能买得起CM300，或者直接租借过瘾即可。&lt;/p&gt;

&lt;p&gt;洗衣机，摔倒报警腕表。洗衣机等回血，腕表太贵了，想着要不二手，要不干脆不要了。&lt;/p&gt;

&lt;p&gt;啊，真正杀死我时间的是脑子里混乱的焦虑，根本不是我喜欢的这些所有。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;写下上面的碎碎念，是在9月9日，如今10月9日有什么变化呢？&lt;/p&gt;

&lt;p&gt;最佳实践的一点是，我重新开始使用谷歌日历了，每天的时间账单很清晰，可以见到自己的精力是分散的。目前在适应这种分散，方法上沿用了习惯的番茄钟，也了解了一下时间拳击法，综合一下适配自己。&lt;/p&gt;

&lt;p&gt;本质还是注意力，其实注意力的训练，我已经摸索了自己多年，但是近年来开始使用抖音的习惯让我开始丧失那种沉浸式的集中感。当然，还有琐碎的生活导致注意力分散。&lt;/p&gt;

&lt;p&gt;还有个较大的缺点是：脑子会有钉子一样的碎碎念。姑且称之为“钉子念想”吧。
这种念头很细小，但是总会像是钉子一样插在脑子里，自己总会不间断的突然想起来，但是又不去做。
实际去执行的话，可能也只是细碎时间内完成的事情。比如：重置忘记了的银行卡密码。&lt;/p&gt;

&lt;p&gt;当前发现最有效的克服“钉子念想”办法是写下来。我忘记哪里有类似的说法和解决办法，看起来有效。&lt;/p&gt;

&lt;p&gt;再回顾一下一个月前的“钉子”，&lt;/p&gt;

&lt;p&gt;DHH演讲看完后，又开始对Rails感兴趣了，下载了Rails的书籍，却又囤着没时间读，成了又一颗钉子。&lt;/p&gt;

&lt;p&gt;Omarchy Linux确实很有意思，我为之投入了小一百元买了U盘，装在了自己的NUC小主机里。在安装过程中体验到了一些问题，均是因为“GFW”导致，好在顺着报错一一解决。&lt;/p&gt;

&lt;p&gt;国际站依然没有上货。好大一颗钉子在脑子里。&lt;/p&gt;

&lt;p&gt;书籍这块，我重新做了分类和整理，我这是知识松鼠除了囤就是整理，乐此不疲。做出的优化是，我的分类开始顺应着自己的规划了，我给自己了三个定位：数据工程师，全栈开发者，创业者。基于这样的定位，我重新整理了书单。我想着每天都回顾一下自己的目标，以便于驱动自己读书。
一个比较有效的尝试，开始听书，不知不觉听完了《创业维艰》。撕碎的时间里，用播客填补空白，可以挽救几百个小时的时间。&lt;/p&gt;

&lt;p&gt;读论文，顺利读完，还是有更深刻的认知，缺乏实践，衍生出的扩展阅读还需要跟进。&lt;/p&gt;

&lt;p&gt;自研大模型基座的软件，可以尝试一下spec-kit啦。&lt;/p&gt;

&lt;p&gt;《凡人修仙传》依然无法割舍，《凸变英雄》追完了，垃圾。《和平使者2》无脑在看。突然想看漫画，下载了《钢之炼金术师》，还是那么喜欢。突然想看《迷宫饭》，神作，喜欢。&lt;/p&gt;

&lt;p&gt;至于花小钱办大事系列，我尝试了之前自己所认为的无意义的花钱，结果没想到还挺香，比如买了一个贴墙白板，比如防滑拖鞋，比如我尝试了阳台种菜，比如冰箱上的计时器，这些事情是那么简单，花销是那么小，但是换来的生活体验是那么多。&lt;/p&gt;

&lt;p&gt;摩托车驾照考试降价到650了，等等党的胜利。&lt;/p&gt;

&lt;p&gt;买了新洗衣机，老婆很满意！&lt;/p&gt;

&lt;p&gt;有了清晰的人生定位，有了果断的行动，从容。&lt;/p&gt;
</description>
        <pubDate>Wed, 10 Sep 2025 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/desires-2025.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/desires-2025.html</guid>
        
        
      </item>
    
      <item>
        <title>超轻量超简单的markdown博客</title>
        <description>&lt;p&gt;我的markdown越攒越多了，有时候又需要给人分享。就想着找个简单轻量的markdown博客系统部署出去。
结果没想到，现在仅仅是把markdown伺服起来这么一个简单的需求，都没有合适的工具了。
大概看了ghost、cms.js等工具，感觉还是不够简单。干脆用AI写个得了。&lt;/p&gt;

&lt;p&gt;在这篇文章中，将展示如何构建一个极简的、超轻量级的 Markdown 博客系统。整个系统依赖于 Python 的 Flask 框架，使用 Markdown 格式的文件作为文章内容，并通过简单的前端页面展示。
它不仅易于部署，而且具备基础的登录功能来保护你的内容。&lt;/p&gt;

&lt;h3 id=&quot;功能概述&quot;&gt;&lt;strong&gt;功能概述&lt;/strong&gt;&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;登录保护&lt;/strong&gt;：确保只有经过验证的用户可以访问 Markdown 文件。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Markdown 渲染&lt;/strong&gt;：将 Markdown 文件转换为 HTML 并展示。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;文件管理&lt;/strong&gt;：列出目录下的所有 Markdown 文件，用户可以点击查看。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;技术栈&quot;&gt;&lt;strong&gt;技术栈&lt;/strong&gt;&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Flask&lt;/strong&gt;：轻量级的 Python Web 框架，用于构建 Web 应用。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Markdown&lt;/strong&gt;：将 Markdown 格式的文本文件转换为 HTML。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;HTML/CSS&lt;/strong&gt;：用于前端展示，没有任何排版。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;搭建步骤&quot;&gt;&lt;strong&gt;搭建步骤&lt;/strong&gt;&lt;/h3&gt;

&lt;h4 id=&quot;1-安装必要的依赖&quot;&gt;1. &lt;strong&gt;安装必要的依赖&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;需要安装 Flask 和 Markdown Python 库。&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;flask markdown
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;2-项目结构&quot;&gt;2. &lt;strong&gt;项目结构&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;项目的文件结构如下所示：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/markdown-blog
    /local-files    # 存放所有的Markdown文件
    /templates      # 存放所有的HTML模板
        index.html
        login.html
        md.html
    app.py           # 主应用文件
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;3-编写-flask-应用apppy&quot;&gt;3. &lt;strong&gt;编写 Flask 应用（app.py）&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;这是整个应用的核心部分。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flask&lt;/code&gt; 用于创建 Web 应用，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;markdown&lt;/code&gt; 用于将 Markdown 文件转换为 HTML，应用还实现了登录保护功能。&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;flask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;markdown&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secret_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;your_secret_key_here&apos;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 设置一个随机密钥
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 配置Markdown文件目录（修改为你的实际路径）
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MD_DIR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__file__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;local-files&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PASSWORD&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;123&apos;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 硬编码密码
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;check_auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;检查是否已登录&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;authenticated&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;/login&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;GET&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;登录页面&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;password&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PASSWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;authenticated&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;index&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;login.html&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;密码错误&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;login.html&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;/&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;显示Markdown文件列表&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check_auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;login&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 获取目录下所有md文件
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;mds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MD_DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endswith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;.md&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# 按文件名排序
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;mds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;index.html&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;/md/&amp;lt;filename&amp;gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;render_md&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;渲染指定Markdown文件&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;check_auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;login&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 防止路径穿越攻击
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;..&apos;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;listdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MD_DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 构建完整文件路径
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MD_DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# 读取并转换Markdown
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;utf-8&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;html_content&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extensions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;extra&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Error reading file: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;md.html&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;html_content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;__main__&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# 确保目录存在
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makedirs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MD_DIR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exist_ok&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;4-创建-html-模板&quot;&gt;4. &lt;strong&gt;创建 HTML 模板&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;index.html&lt;/strong&gt;（显示所有 Markdown 文件的列表）：&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Markdown文件列表&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;800px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#0366d6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:hover&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f6f8fa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;选择要查看的Markdown文件：&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;目录下没有Markdown文件&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;login.html&lt;/strong&gt;（用于登录页面）：&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;登录&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;请输入访问密码&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;password&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;password&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;登录&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;md.html&lt;/strong&gt;（显示 Markdown 文件内容）：&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;\{\{ filename \}\}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;800px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;pre&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f6f8fa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;code&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SFMono-Regular&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Consolas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Menlo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;monospace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;blockquote&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;border-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#dfe2e5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#6a737d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;border-collapse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;collapse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;th&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;td&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#dfe2e5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;13px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\{\{ url_for(&apos;index&apos;) \}\}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;← 返回列表&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;markdown-content&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        \{\{ content|safe \}\}
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;5-运行应用&quot;&gt;5. &lt;strong&gt;运行应用&lt;/strong&gt;&lt;/h4&gt;

&lt;p&gt;在项目目录下执行以下命令启动 Flask 应用：&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;应用将运行在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://127.0.0.1:5000&lt;/code&gt;，你可以通过浏览器访问它。&lt;/p&gt;

&lt;h4 id=&quot;6-如何使用&quot;&gt;6. &lt;strong&gt;如何使用&lt;/strong&gt;&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;将你的 Markdown 文件（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.md&lt;/code&gt; 格式）放在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local-files&lt;/code&gt; 目录下。&lt;/li&gt;
  &lt;li&gt;访问首页并输入正确的密码登录。&lt;/li&gt;
  &lt;li&gt;登录后，你可以浏览所有 Markdown 文件，点击链接查看文件内容。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;总结&quot;&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;这个简单的 Markdown 博客系统是一个非常基础的项目，适合想要搭建轻量博客的开发者。你可以通过自定义 Markdown 文件目录、密码保护等，满足个人博客的需求。因为是 Flask 应用，你还可以根据需求进一步扩展功能，比如添加评论系统、分页功能等。&lt;/p&gt;
</description>
        <pubDate>Fri, 18 Apr 2025 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/markdown-blog.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/markdown-blog.html</guid>
        
        
      </item>
    
      <item>
        <title>回想2024的思维导图</title>
        <description>&lt;p&gt;无意间翻出2024年期间，热火朝天干工作，脑子发热时候写下的MindMap。
当时脑子真是太热了，想通过思维导图方式整理下思绪。
现在再回头看，往事东流水，笑谈也不成了。 
那时候的情绪，现在想细微去品觉，一分难以回应。&lt;/p&gt;

&lt;p&gt;彼时大概情景是，机缘巧合在线结识一个号称类似摩根士丹利什么鬼的金融大鳄归国。
GPT横空出世，引发巨大商机，作为金融弄潮儿想在中国商海引起一波涟漪。我自觉时运已到，赶快合作一波。一来二去，就张罗着开始搞创业了。&lt;/p&gt;

&lt;p&gt;MindMap中，写下的“核心任务”大概有这些项，第一大主题里是想弄个纯AI SKU的淘宝店：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;项目&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;当时怎么想&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;现在怎么想&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;淘宝店&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;这淘宝店可是不一般。当时GPT新鲜劲儿刚来，就想张罗着给人去充值4.0账户。淘宝上挂出这个商品，还是有商机在的。GPT 4.0账户 、企业版席位这关键字上去，就是绝佳爆品。金融大鳄憋了大的说他弟弟在OpenAI工作，意图拿下中国金牌代理席位。哥们儿是真敢说呀。&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;还是惊叹阿里巴巴的配合程度，品类分类有问题，反馈后立马给作出了调整。GPT账号充值，我也终于找到了稳定的方法。至于API黑色产业，也有一定了解。作为Tire 4玩家，这方面已经没有什么疑问了。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;DEEP LEARNING 深度学习代做&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;作为中国AI界的吃螃蟹者(自诩的)，当然得深入技术底层，什么机器学习、深度学习，已经是工业场景成熟的产物，一顿忽悠直接上，回头再想调用什么API。&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;当时是作为技术人员的拧巴，觉得这种服务必须上。其实放到今天，心底也只有一个人选能做。自己半斤八两只懂个皮毛，10年前就想学，也却是学了，也却是忘了。未来遇到这样的技术需求，默认去调用大厂API，这样ROI更高。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;BID 标书自动生成&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;大模型做标书生成，一键直接出标书全文，可谓是大模型落地之首。这个产品是直接找一个叫阿然的网友采购的，他也是二道贩子。忘了因为什么，还产生了些纠纷，把服务器给停了。&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;涉及到标书，核心原因还是股东里有个小伙子公司是做标书服务的。我们后续也做了真金白银的落地测试。目前最终情况是，培训标书专员去使用ChatGPT成本最低。这也是去年失败投资里最成功的一件事之一，当然了，也没有什么收益。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;代购PIXCEL 8&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;AI手机，不必多说。&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;手机内嵌大模型功能已经普及，我已经非常习惯了，没想仅仅几百天前，真正的AI手机只有一台P8能看得上眼。这里金融大鳄曾说要每人赠送一部，然并无。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;AI生成服装、盲盒&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;没记错的话，是Stable Diffution，当时通过生成做出一个不错的人物形象，甚至计划出实体手办。&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;在线测试服装换衣，在云栖大会里见到了实际产品，也有联系试用，无后续。盲盒的话，确实可行，一个叫阿威的哥们做了个原形，还挺好看。一高兴送了一条挺贵的领带，妈的。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;厂商合作&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;科大讯飞办公硬件， 飞虎的文字生图。&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;科大会议耳机看起来目前已经推广的不错。文生图主要是想与杭州的一家公司合作，这公司目前看做得不错，只记得CEO有不少比特币。可惜我没维护这条关系线。我觉得金融大鳄一直马扁这家公司，就没好意思联系人家。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;数字人&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;印象中大模型爆火后，跟着数字人就火了，大家都想着说数字人接入大模型。&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;硅基智能作为大营销公司活到现在也真是骗不知耻，现在还有个朋友在跟这个项目，大家都是个API二道贩子，没啥技术含量。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;VR设备&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;印象中是想卖Meta的VR设备&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;无后续，一切关于硬件采买的，金融大鳄都跳票了。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;泡塑机料枪&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;对接了一家工厂，想将AI接入到工厂的工作流程中提效&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;我当时想到去训练ChatBot给业务员用，在导出客户资料那天，厂长和厂长夫人紧急叫停了，他们担心客户外泄。这个厂目前还有保持在联系。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;赶着双11当日，我们结合各类AI工具，花了1天就把这个淘宝店上线了。草台班子也挺有行动力。
这篇博客我写了一半，其实就不太想写了。做得失败事情挺多，挺没意思的。做商业这些人都挺虚伪的。
金融大鳄满口AI，其实都不知道artificial intelligence怎么拼。不过人家今年却是骗出点成绩，看样子常州套了个办公楼。&lt;/p&gt;

&lt;p&gt;MindMap第二大主题是，会所小程序。此事源头还是与合作的金融大鳄有关，他说自己在上海的会所想弄个小程序做会员卡管理。我一琢磨，这可行啊，淘宝花钱给搭个。
上海会所的老钱风和老土风是并重的，X公馆。这哥们儿进了会所就暴露了，这哪是他开的，只能说确实是千丝万缕的联系吧。
这会所老大倒是有点意思，平时爱叼着一根雪茄，第一次见面的时候，正在纠结新买的摇表器柜子摆到哪里。
我们大概磋商了一下小程序设计风格，我说宝玑风吧，他连连称赞。
此事结束的时间点是在之后了，金融大鳄暗戳戳跟我说，你给报价XX万吧（高报5倍），多余的钱我们分。&lt;/p&gt;

&lt;p&gt;MindMap第三大主题是，“APEC 青岛 1月1日前确定”，哈哈哈，我是做梦都没想到，参加个国际级会议的门槛，其实还挺低的。不过最终还是没去参加的，这tmd的怎么去嘛，展示个球。&lt;/p&gt;

&lt;p&gt;2024年，总的来说就是祛魅的一年。彻底祛魅了。现在遇到一个商海大佬，我怼一个。但是经历这些，也没让我长太多记性。我现在又陷入了下一场故事之中。
为什么会偶然打开这个MindMap呢，因为我是要写新的MindMap整理新的思绪。&lt;/p&gt;

&lt;p&gt;不缺商机，闭着眼睛都有商机。
骗子最终套路都一致——借假修真。&lt;/p&gt;

&lt;p&gt;我们无产阶级真可怜，活在既得利益者没怎么上心构建的局中。&lt;/p&gt;
</description>
        <pubDate>Tue, 21 Jan 2025 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/review-2024.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/review-2024.html</guid>
        
        
      </item>
    
      <item>
        <title>考驾照一二事</title>
        <description>&lt;p&gt;8月底时，家父见我闲得发慌，终于强推到驾校去学习。我老张家自从老老张就是老司机，到了老张更是跟车打了一辈子交道，如今到我竟然驾照都没，简直不能忍。&lt;/p&gt;

&lt;p&gt;没有驾照——简直是心病。&lt;/p&gt;

&lt;p&gt;对于开车，我是确实没啥兴趣，一是买不起，二是打工打到骨子里，根本没有用车场景，叫个网约车对我来说都是奢侈行为了。
也罢，总不能虚度时光，回顾2024年，新技术学习不能仅仅是会更好的写提示词。
这进了驾校才知道，多少人对于取到驾照竟然是如此执念。从高三毕业党到退休大妈，一天到晚都在驾校孜孜不倦。&lt;/p&gt;

&lt;p&gt;我不太当回事，权当打发时间，心理压力一直不足。科目一刷题两日，找回点学习的感觉，果断下单了驾校一点通，想着速战速决。奔着满分去，没想到有几道题牵扯到营运车辆问题，是个知识盲区，遗憾90分晋级。&lt;/p&gt;

&lt;p&gt;为了省钱，一切都是为了省钱，在我们内蒙古考个驾照才2000。&lt;/p&gt;

&lt;p&gt;科目二烈日炎炎，排队的车友太多，总是觉得练得不过瘾，教练主张一个无师自通，大道至简，自己刷抖音学得奇技淫巧一律不管用，全凭教练点位点拨以及极简操作。家父直接出阵教学，借了车拉着一家子在废弃广场里一轮一轮练习。自信练得不错，手动挡不过如此。&lt;/p&gt;

&lt;p&gt;怎奈，科目二第一场就挂了，开始考试指令一发，直接启动——没有系安全带。是的，我仔细回顾自己的用车历史，老爹没系过，教练没系过，骨子里就没意识到这是个考点。第二次机会，在侧方停车项目中，停车点位不对，再挂。&lt;/p&gt;

&lt;p&gt;“啊，教练，你确实没教过啊，你要说一声我也不至于挂。”&lt;/p&gt;

&lt;p&gt;科目二不得已间隔十日后再战。补考费200。&lt;/p&gt;

&lt;p&gt;好不容易熬到科三，已经开始有了车瘾，方向盘上手后有点小兴奋。膨胀到已经开始看车，基本锁定想弄个马自达练手，没想到买车竟然如此便宜，虽然荷包空空，但已经基本锁定要来一辆二手手动挡玩玩。&lt;/p&gt;

&lt;p&gt;只是没想到，科目三是噩梦的开始，压力逐渐显现。不知不觉挂了三轮了：&lt;/p&gt;

&lt;p&gt;一挂在,教练车和考试车车型不一样，实在搞不懂考试车的判定逻辑，明明自己感觉没问题的操作，考试机器就判定为失误操作。&lt;/p&gt;

&lt;p&gt;补考费累计：400。&lt;/p&gt;

&lt;p&gt;二挂在，教练追求的大道至简实在不适合于我等资质平凡之辈，比如我是挂了后才知道原来刹车需要在一个要求的范围内。&lt;/p&gt;

&lt;p&gt;补考费累计：600。&lt;/p&gt;

&lt;p&gt;家父家母施压，给教练红包：300。&lt;/p&gt;

&lt;p&gt;三挂在，考试过程中路边临时施工，我就按照抖音中学到的操作得意洋洋的打双闪静候近半个小时，全考场中唯一一个正确处理的考生。怎奈何，怎奈何，重新给车打火时，离合死活找不到点，熄火两次，挂。&lt;/p&gt;

&lt;p&gt;补考费累计：800。&lt;/p&gt;

&lt;p&gt;三战科目三，终于开始感受到驾照的痛苦与纠缠。伴随着这开车的兴奋感，期望感，死死环绕着我。近乎病态啊，白天纠结到底买马自达还是买斯巴鲁，晚上纠结驾照还没到手，想这么多干嘛。&lt;/p&gt;

&lt;p&gt;最讽刺的是，科目三考场，就是我打小长大的那条街。&lt;/p&gt;

&lt;p&gt;研究二手车，研究手动挡技巧，研究玉皇大帝都开不走的抵押车……然后回到现实继续看科目三技巧。&lt;/p&gt;

&lt;p&gt;现在考驾照的钱，已经够我买一台二手车了。&lt;/p&gt;

&lt;p&gt;事情的转机到了，夫人哀怨我既然考不过，为什么不能找到私教带带，果断把我丢到杭州场口驾校附近找私教。&lt;/p&gt;

&lt;p&gt;杭州加课练车费：150。&lt;/p&gt;

&lt;p&gt;何教练，我遇到你，简直是三生有幸！&lt;/p&gt;

&lt;p&gt;在我自信满满把车点着时，杭州场口驾校老董便利店良心专家何教练脸上已经三条黑线：“照你这么考，开始就该挂了呀。”&lt;/p&gt;

&lt;p&gt;“我们那边考，这样就行啊。” 我有点想找补一下。&lt;/p&gt;

&lt;p&gt;“不行！”，随后就是不知不觉一个小时的精彩教学。我的亲娘，对比起来，我驾校的教练简直就是什么都没教我。原来科目三虽然简单，但是还是有很多点位需要注意。&lt;/p&gt;

&lt;p&gt;原来手动挡的离合器,是可以不卡顿启动车辆的: 缓抬离合器，在车身微微震动时，要持续几秒，然后松开刹车。&lt;/p&gt;

&lt;p&gt;在加减档位时候，要留够间隔时间，是可以踩一下油门补油，让转速提上来。&lt;/p&gt;

&lt;p&gt;我终于真正的开始学习了一次驾驶。&lt;/p&gt;

&lt;p&gt;果断再多加练1小时，杭州加课练车费累计：300。&lt;/p&gt;

&lt;p&gt;原来老天让我挂三次是为了我好啊，我根本就没学驾驶，挂了也是应该的。&lt;/p&gt;

&lt;p&gt;10月，12日。张某将会再重返内蒙古重战科目三。&lt;/p&gt;

&lt;p&gt;路费：1000+。&lt;/p&gt;

</description>
        <pubDate>Sun, 29 Sep 2024 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/driver-license.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/driver-license.html</guid>
        
        
      </item>
    
      <item>
        <title>关于最近的我</title>
        <description>&lt;p&gt;我完整的休息了一年。&lt;/p&gt;

&lt;p&gt;是的，脱离工作苦海。&lt;/p&gt;

&lt;p&gt;不得不承认高强度工作带来的职业倦怠感是致命的。我真诚的建议任何从业者都不要过度投入到别人制作的世界中。真的会PTSD。&lt;/p&gt;

&lt;p&gt;在休息的这一年中，经历了很多职业外的故事，反而让我更加开始知道人间的滋味。
对社会、对人间百态的体验，是一个祛魅的过程。&lt;/p&gt;

&lt;p&gt;——我逐渐开始理解一切。&lt;/p&gt;

&lt;p&gt;各自生死执念中,&lt;/p&gt;

&lt;p&gt;皮囊掩下心中闷,&lt;/p&gt;

&lt;p&gt;哭笑付于匆匆。&lt;/p&gt;

&lt;p&gt;到处都是疲劳干瘪的人，在驯化自己这件事情上，我们真的投入了很多。
休息这一年，大概最大的收获，就是跳出了这种奇怪的圈子开始审视所有问题。&lt;/p&gt;

&lt;p&gt;离开一份消耗你生命的工作。&lt;/p&gt;

&lt;p&gt;对别人说不。&lt;/p&gt;

&lt;p&gt;退出社交媒体，关注生活。&lt;/p&gt;

&lt;p&gt;忽视他人言论，做对自己最好的事情。&lt;/p&gt;

</description>
        <pubDate>Wed, 25 Sep 2024 00:00:00 +0800</pubDate>
        <link>https://yangcongchufang.com/recent-about-me.html</link>
        <guid isPermaLink="true">https://yangcongchufang.com/recent-about-me.html</guid>
        
        
      </item>
    
  </channel>
</rss>
