<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>第一章 系统概览 on Machine Learning 学习笔记</title><link>https://kingye.me/study-ml/docs/book/sglang-internals/part1-prerequisite/overview/</link><description>Recent content in 第一章 系统概览 on Machine Learning 学习笔记</description><generator>Hugo</generator><language>en</language><atom:link href="https://kingye.me/study-ml/docs/book/sglang-internals/part1-prerequisite/overview/index.xml" rel="self" type="application/rss+xml"/><item><title>1.1 SGLang 到底是什么</title><link>https://kingye.me/study-ml/docs/book/sglang-internals/part1-prerequisite/overview/what-sglang-is/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kingye.me/study-ml/docs/book/sglang-internals/part1-prerequisite/overview/what-sglang-is/</guid><description>&lt;h1 id="sglang-到底是什么"&gt;SGLang 到底是什么&lt;a class="anchor" href="#sglang-%e5%88%b0%e5%ba%95%e6%98%af%e4%bb%80%e4%b9%88"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;这一节先解决整本书最基础的问题：SGLang 到底应该被看成什么系统。只有这个问题先回答清楚，后面的请求路径、调度器和执行壳才不会被误读成彼此独立的局部实现。&lt;/p&gt;
&lt;h2 id="它首先不是一个单一人格的项目"&gt;它首先不是一个单一人格的项目&lt;a class="anchor" href="#%e5%ae%83%e9%a6%96%e5%85%88%e4%b8%8d%e6%98%af%e4%b8%80%e4%b8%aa%e5%8d%95%e4%b8%80%e4%ba%ba%e6%a0%bc%e7%9a%84%e9%a1%b9%e7%9b%ae"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;很多系统对外只有一种主要形态，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个单纯的 HTTP server&lt;/li&gt;
&lt;li&gt;一个单纯的 SDK&lt;/li&gt;
&lt;li&gt;一个单纯的离线 engine&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SGLang 不是这样。只要顺着最外层入口看一遍，你很快就会发现它同时暴露了三种不同的人格：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;language API&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;python/sglang/lang/api.py&lt;/code&gt; 里的 &lt;code&gt;gen(...)&lt;/code&gt;、&lt;code&gt;user(...)&lt;/code&gt;、&lt;code&gt;assistant(...)&lt;/code&gt;、&lt;code&gt;system(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;runtime / engine API&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Engine&lt;/code&gt;、&lt;code&gt;RuntimeEndpoint&lt;/code&gt;、&lt;code&gt;ServerArgs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;serving surface&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;sglang serve&lt;/code&gt;、&lt;code&gt;python -m sglang.launch_server&lt;/code&gt;、OpenAI-compatible HTTP 接口&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;也就是说，SGLang 不是”先有一个 server，后来顺手加了点 API”；它从一开始就是一套同时面向 prompt program、runtime 承载和服务暴露的系统。&lt;/p&gt;
&lt;h2 id="三种入口的实际代码对比"&gt;三种入口的实际代码对比&lt;a class="anchor" href="#%e4%b8%89%e7%a7%8d%e5%85%a5%e5%8f%a3%e7%9a%84%e5%ae%9e%e9%99%85%e4%bb%a3%e7%a0%81%e5%af%b9%e6%af%94"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;先看三段可以真正运行的代码，再回来看它们共享什么。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;入口一：Language API（描述式编程）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用 &lt;code&gt;@sgl.function&lt;/code&gt; 把生成逻辑写成程序：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; sglang &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; sgl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@sgl.function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;classify_sentiment&lt;/span&gt;(s, text):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; s &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; sgl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;system(&lt;span style="color:#960050;background-color:#1e0010"&gt;“&lt;/span&gt;You are a classifier&lt;span style="color:#f92672"&gt;.&lt;/span&gt; Reply &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; one word: positive&lt;span style="color:#f92672"&gt;/&lt;/span&gt;negative&lt;span style="color:#f92672"&gt;/&lt;/span&gt;neutral&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;”&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; s &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; sgl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;user(text)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; s &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; sgl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;assistant(sgl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;gen(&lt;span style="color:#960050;background-color:#1e0010"&gt;“&lt;/span&gt;label&lt;span style="color:#960050;background-color:#1e0010"&gt;”&lt;/span&gt;, max_new_tokens&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, regex&lt;span style="color:#f92672"&gt;=&lt;/span&gt;r&lt;span style="color:#960050;background-color:#1e0010"&gt;”&lt;/span&gt;positive&lt;span style="color:#f92672"&gt;|&lt;/span&gt;negative&lt;span style="color:#f92672"&gt;|&lt;/span&gt;neutral&lt;span style="color:#960050;background-color:#1e0010"&gt;”&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;state &lt;span style="color:#f92672"&gt;=&lt;/span&gt; classify_sentiment&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run(text&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;”&lt;/span&gt;The food was amazing&lt;span style="color:#960050;background-color:#1e0010"&gt;!”&lt;/span&gt;, backend&lt;span style="color:#f92672"&gt;=&lt;/span&gt;sgl&lt;span style="color:#f92672"&gt;.&lt;/span&gt;RuntimeEndpoint(&lt;span style="color:#960050;background-color:#1e0010"&gt;“&lt;/span&gt;http:&lt;span style="color:#f92672"&gt;//&lt;/span&gt;localhost:&lt;span style="color:#ae81ff"&gt;30000&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;”&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(state[&lt;span style="color:#960050;background-color:#1e0010"&gt;“&lt;/span&gt;label&lt;span style="color:#960050;background-color:#1e0010"&gt;”&lt;/span&gt;]) &lt;span style="color:#75715e"&gt;# → “positive”&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键特征：&lt;code&gt;@sgl.function&lt;/code&gt; 把生成意图（包括结构化约束 &lt;code&gt;regex&lt;/code&gt;）直接写成 Python 函数，执行时异步分派给 runtime。&lt;/p&gt;</description></item><item><title>1.2 为什么不能只把它看成一个 server</title><link>https://kingye.me/study-ml/docs/book/sglang-internals/part1-prerequisite/overview/why-not-just-a-server/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kingye.me/study-ml/docs/book/sglang-internals/part1-prerequisite/overview/why-not-just-a-server/</guid><description>&lt;h1 id="为什么不能只把它看成一个-server"&gt;为什么不能只把它看成一个 server&lt;a class="anchor" href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e8%83%bd%e5%8f%aa%e6%8a%8a%e5%ae%83%e7%9c%8b%e6%88%90%e4%b8%80%e4%b8%aa-server"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;很多人第一次接触 SGLang，会先把它放进“高性能推理服务”的类别里。这个切入点不完全错，但如果只停在这里，后面就会很难理解语言层 API、结构化生成和 engine 嵌入式用法为什么会和 serving 共享同一套内部对象。&lt;/p&gt;
&lt;h2 id="像-server-的部分当然存在"&gt;“像 server” 的部分当然存在&lt;a class="anchor" href="#%e5%83%8f-server-%e7%9a%84%e9%83%a8%e5%88%86%e5%bd%93%e7%84%b6%e5%ad%98%e5%9c%a8"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;从外部表面看，把 SGLang 当作 server 并不奇怪：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你可以直接 &lt;code&gt;sglang serve&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;可以用 OpenAI-compatible HTTP 接口请求它&lt;/li&gt;
&lt;li&gt;它有路由、middleware、metrics、API key 和健康检查&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以“它像一个 server”这件事本身没有问题。问题在于，如果把这个结论升级成“它本质上只是一个 server”，你后面就会不断遇到解释不通的地方。&lt;/p&gt;
&lt;h2 id="哪些现象说明它不只是-server"&gt;哪些现象说明它不只是 server&lt;a class="anchor" href="#%e5%93%aa%e4%ba%9b%e7%8e%b0%e8%b1%a1%e8%af%b4%e6%98%8e%e5%ae%83%e4%b8%8d%e5%8f%aa%e6%98%af-server"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最直接的几个现象是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;language API 是一等入口&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;gen(...)&lt;/code&gt;、&lt;code&gt;system(...)&lt;/code&gt;、&lt;code&gt;user(...)&lt;/code&gt; 并不是文档装饰，它们是系统正式暴露的编程接口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;engine / runtime 也是一等入口&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Engine(...)&lt;/code&gt;、&lt;code&gt;RuntimeEndpoint(...)&lt;/code&gt; 说明系统并不要求所有用法都绕 HTTP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结构化约束直接进入生成主链&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;regex&lt;/code&gt;、&lt;code&gt;json_schema&lt;/code&gt;、tool calling 并不是 server 外层附加功能，而会一路落到采样与执行路径。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些现象都指向同一件事：server 只是 SGLang 的一个对外表面，而不是它唯一的系统定义。&lt;/p&gt;
&lt;h2 id="这种误读会具体带来什么后果"&gt;这种误读会具体带来什么后果&lt;a class="anchor" href="#%e8%bf%99%e7%a7%8d%e8%af%af%e8%af%bb%e4%bc%9a%e5%85%b7%e4%bd%93%e5%b8%a6%e6%9d%a5%e4%bb%80%e4%b9%88%e5%90%8e%e6%9e%9c"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果把它只看成 server，后面最常见的后果有三个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把本应在 language API 理解的问题，硬塞回 HTTP request 视角；&lt;/li&gt;
&lt;li&gt;把 &lt;code&gt;GenerateReqInput&lt;/code&gt;、&lt;code&gt;Req&lt;/code&gt;、&lt;code&gt;ForwardBatch&lt;/code&gt; 这类运行时对象，误看成“HTTP 请求在内部的变种”；&lt;/li&gt;
&lt;li&gt;把结构化生成、tool parser 和 Responses API 都看成“接口特性”，而忽略它们实际上深入到了 generation path。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一旦发生这三种误读，后面的章节虽然还能各自读懂，但整本书的主线会断。&lt;/p&gt;</description></item><item><title>1.3 全书主线、边界与核心不变量</title><link>https://kingye.me/study-ml/docs/book/sglang-internals/part1-prerequisite/overview/book-scope-and-invariants/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kingye.me/study-ml/docs/book/sglang-internals/part1-prerequisite/overview/book-scope-and-invariants/</guid><description>&lt;h1 id="全书主线边界与核心不变量"&gt;全书主线、边界与核心不变量&lt;a class="anchor" href="#%e5%85%a8%e4%b9%a6%e4%b8%bb%e7%ba%bf%e8%be%b9%e7%95%8c%e4%b8%8e%e6%a0%b8%e5%bf%83%e4%b8%8d%e5%8f%98%e9%87%8f"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;这一节负责把全书真正反复回扣的主线钉住。它不会提前展开全部实现，但会说明哪些对象、边界和运行时语义构成了后面章节共享的底板。&lt;/p&gt;
&lt;h2 id="这本书写什么不写什么"&gt;这本书写什么，不写什么&lt;a class="anchor" href="#%e8%bf%99%e6%9c%ac%e4%b9%a6%e5%86%99%e4%bb%80%e4%b9%88%e4%b8%8d%e5%86%99%e4%bb%80%e4%b9%88"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;这本书写的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;请求怎样进入、排队、执行、回包；&lt;/li&gt;
&lt;li&gt;这些路径在源码里分别落在哪些对象和文件上；&lt;/li&gt;
&lt;li&gt;当系统出问题时，应该先回到哪条主线和哪组证据。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这本书不写的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有模型、所有 backend、所有实验分支的百科式覆盖；&lt;/li&gt;
&lt;li&gt;未经验证的性能结论；&lt;/li&gt;
&lt;li&gt;把目录结构直接当作架构真相的强断言。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;换句话说，这本书追求的是“稳定主线 + 稳定锚点”，不是“覆盖一切可能的局部知识”。&lt;/p&gt;
&lt;h2 id="全书反复回扣的主线"&gt;全书反复回扣的主线&lt;a class="anchor" href="#%e5%85%a8%e4%b9%a6%e5%8f%8d%e5%a4%8d%e5%9b%9e%e6%89%a3%e7%9a%84%e4%b8%bb%e7%ba%bf"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果把全书压缩成一条最短主线，它其实就是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;请求怎样进入系统；&lt;/li&gt;
&lt;li&gt;请求怎样被压成运行时对象；&lt;/li&gt;
&lt;li&gt;请求怎样进入 queue、cache、execution；&lt;/li&gt;
&lt;li&gt;结果怎样回到协议表面；&lt;/li&gt;
&lt;li&gt;出了问题以后，证据链怎样重新回到这些对象和边界上。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这条主线之所以重要，是因为它决定了后面的章节顺序：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第二部分先走通请求路径；&lt;/li&gt;
&lt;li&gt;第三部分再进入执行核心；&lt;/li&gt;
&lt;li&gt;第四部分最后才回到代码导读、观测和维护。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果没有这条主线，后面的章节就会更像平铺专题，而不像一本系统书。&lt;/p&gt;
&lt;h2 id="哪些不变量必须在后文持续成立"&gt;哪些不变量必须在后文持续成立&lt;a class="anchor" href="#%e5%93%aa%e4%ba%9b%e4%b8%8d%e5%8f%98%e9%87%8f%e5%bf%85%e9%a1%bb%e5%9c%a8%e5%90%8e%e6%96%87%e6%8c%81%e7%bb%ad%e6%88%90%e7%ab%8b"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;后面无论写哪一章，至少有四条东西应该持续成立。&lt;/p&gt;
&lt;h3 id="不变量一请求对象会变形但不会失去边界"&gt;不变量一：请求对象会变形，但不会失去边界&lt;a class="anchor" href="#%e4%b8%8d%e5%8f%98%e9%87%8f%e4%b8%80%e8%af%b7%e6%b1%82%e5%af%b9%e8%b1%a1%e4%bc%9a%e5%8f%98%e5%bd%a2%e4%bd%86%e4%b8%8d%e4%bc%9a%e5%a4%b1%e5%8e%bb%e8%be%b9%e7%95%8c"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;请求会一路变化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ChatCompletionRequest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GenerateReqInput&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReqState&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TokenizedGenerateReqInput&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Req&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ScheduleBatch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ForwardBatch&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但每一层都必须仍然有明确职责。后文如果把这些对象混着讲，这条不变量就被破坏了。&lt;/p&gt;
&lt;h3 id="不变量二控制面和数据面必须分得开"&gt;不变量二：控制面和数据面必须分得开&lt;a class="anchor" href="#%e4%b8%8d%e5%8f%98%e9%87%8f%e4%ba%8c%e6%8e%a7%e5%88%b6%e9%9d%a2%e5%92%8c%e6%95%b0%e6%8d%ae%e9%9d%a2%e5%bf%85%e9%a1%bb%e5%88%86%e5%be%97%e5%bc%80"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;session、abort、priority、weight update、logging configuration 这些更像控制面；batch、KV cache、forward、sampling、text delta 则更像数据面。两者会互相影响，但不应该被写成同一个层次的问题。&lt;/p&gt;
&lt;h3 id="不变量三运行时边界比目录边界更重要"&gt;不变量三：运行时边界比目录边界更重要&lt;a class="anchor" href="#%e4%b8%8d%e5%8f%98%e9%87%8f%e4%b8%89%e8%bf%90%e8%a1%8c%e6%97%b6%e8%be%b9%e7%95%8c%e6%af%94%e7%9b%ae%e5%bd%95%e8%be%b9%e7%95%8c%e6%9b%b4%e9%87%8d%e8%a6%81"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;后面不断会出现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TokenizerManager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Scheduler&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DetokenizerManager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ModelRunner&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些边界不是因为目录刚好这么分，而是因为运行时真的靠这些边界在推进状态。全书后面必须一直把它们当成正式边界，而不是方便记忆的模块名。&lt;/p&gt;
&lt;h3 id="不变量四观测和证据必须能回到主线"&gt;不变量四：观测和证据必须能回到主线&lt;a class="anchor" href="#%e4%b8%8d%e5%8f%98%e9%87%8f%e5%9b%9b%e8%a7%82%e6%b5%8b%e5%92%8c%e8%af%81%e6%8d%ae%e5%bf%85%e9%a1%bb%e8%83%bd%e5%9b%9e%e5%88%b0%e4%b8%bb%e7%ba%bf"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;RequestStage&lt;/code&gt;、&lt;code&gt;ReqTimeStats&lt;/code&gt;、request logger、trace、dump、profile、tests 这些内容，后面都不应该变成“独立工具箱”。它们存在的意义，是把排障和维护重新接回请求主线和运行时边界。&lt;/p&gt;
&lt;h2 id="读每章时都该反复问的三个问题"&gt;读每章时都该反复问的三个问题&lt;a class="anchor" href="#%e8%af%bb%e6%af%8f%e7%ab%a0%e6%97%b6%e9%83%bd%e8%af%a5%e5%8f%8d%e5%a4%8d%e9%97%ae%e7%9a%84%e4%b8%89%e4%b8%aa%e9%97%ae%e9%a2%98"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;为了把这组不变量真正变成阅读工具，而不是抽象口号，可以把它压成三个固定问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;这一章主要保护哪条不变量？&lt;/li&gt;
&lt;li&gt;如果这条不变量被打破，系统会先表现成什么症状？&lt;/li&gt;
&lt;li&gt;哪个源码锚点最能证明这条不变量存在？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;只要对每章都能回答这三个问题，整本书就不容易被读散。&lt;/p&gt;
&lt;h2 id="为什么这一节必须放在最前面"&gt;为什么这一节必须放在最前面&lt;a class="anchor" href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%bf%99%e4%b8%80%e8%8a%82%e5%bf%85%e9%a1%bb%e6%94%be%e5%9c%a8%e6%9c%80%e5%89%8d%e9%9d%a2"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;如果这些范围和不变量不先说清楚，后面读到某一章时你很容易犯两种错误：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;看到局部细节就把它当成全书中心；&lt;/li&gt;
&lt;li&gt;看到后面的证据链又把它当成新的旁支专题。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;把不变量先立起来的好处，是后面每一章都知道自己在护哪一层，不需要临时再给自己找存在理由。&lt;/p&gt;</description></item></channel></rss>