<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>AI 编程</title><link>https://aicoding.juejin.cn/?contentType=2</link><atom:link href="http://rss.144-124-237-35.sslip.io/juejin/aicoding" rel="self" type="application/rss+xml"></atom:link><description>AI 编程 - Powered by AtomRSS</description><generator>AtomRSS</generator><webMaster>contact@atomgroup.dev (AtomRSS)</webMaster><language>en</language><image><url>https://lf-web-assets.juejin.cn/obj/juejin-web/goofy_deploy_edenx/toutiao-fe/xitu_juejin_aicoding/favicon.ico</url><title>AI 编程</title><link>https://aicoding.juejin.cn/?contentType=2</link></image><lastBuildDate>Sat, 08 Aug 2026 07:34:30 GMT</lastBuildDate><ttl>5</ttl><item><title>我用agent做了个运动打卡数据治理流程</title><description>&lt;h2 data-id=&quot;heading-0&quot;&gt;介绍&lt;/h2&gt;
&lt;p&gt;公司举办了一项运动打卡活动，报名成员每天完成运动后，会把运动 App 截图和相关说明发到运动聊天群。工作人员需要定期整理群里的打卡记录，统计每位成员的运动时间和运动时长。&lt;/p&gt;
&lt;p&gt;随着群聊资料不断增加，人工逐张查看截图、判断有效记录和抄录数据会花费不少时间。如何减少这些重复工作，快速得到可以统计的运动打卡数据，就是这次要解决的问题。&lt;/p&gt;
&lt;p&gt;为此，我做了一套运动打卡数据处理流程。它会下载群聊归档，识别截图和上下文，整理成结构化数据，再把异常记录交给人工核查。最近我又用 TRAE Work 在 sportcheckin 工程中完整跑了一遍这套流程，确认 Agent 可以完成资料下载、内容识别和数据输出。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/dee1a2bd3f494ddeb9960f11ef29e9f3~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=usmddV%2BO1GKy2ysGL%2FzbBcV8Wpo%3D&quot; alt=&quot;00-sportcheckin-workflow-v2.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h2 data-id=&quot;heading-1&quot;&gt;主要痛点&lt;/h2&gt;
&lt;p&gt;原来的做法是人工逐张看图。管理员需要一张张图片，再判断图片是不是运动记录，抄下运动时间和时长，最后汇总统计。难点不在单张截图，而在这些细节会叠在一起。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不同运动 App 的版式不同，人眼识别有概率出错&lt;/li&gt;
&lt;li&gt;运动时间和发送到群里的提交时间可能不同需要人工甄别&lt;/li&gt;
&lt;li&gt;群里有补交、重复图、排行榜和普通聊天内容需要人工甄别&lt;/li&gt;
&lt;li&gt;如果还想统计运动类型、卡路里，人工录入的工作量会继续上涨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6525912078ec4c118f478b39b6692ce7~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=WtK3xtUoah7uQGGT%2BZYfa0elEYk%3D&quot; alt=&quot;Pasted image 20260808103250.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h2 data-id=&quot;heading-2&quot;&gt;要解决的问题&lt;/h2&gt;
&lt;p&gt;我需要做的事情是把每天群里哪些繁杂的聊天记录整理为一个结构化、可追溯的数据表，我的同事已经开发了工具，可以每天定时把聊天内容图文上传内部云盘，我只要通过API下载文件，并输出分析后的干净数据即可。&lt;/p&gt;
&lt;p&gt;我要拿到的不只是一张总表。每条记录要保留各种运动信息、原图地址、人工核查状态。最后输出 CSV格式的文件、识别报告和待人工复核清单。一开始我以为只要做个工具获取聊天记录docx 丢给 AI，让它直接输出表格就行。结果跑起来才发现，图片多时速度慢，中间哪里错了也不好找，后面人工复核更没法追溯。&lt;/p&gt;
&lt;p&gt;所以调整了工作流程，给 TRAE Work 下发任务前，没有写成一句“帮我整理运动记录”，而是先把我具体的需求，输入、输出和异常规则说清楚。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot; lang=&quot;markdown&quot;&gt;请处理指定日期范围内的运动群归档文件。

任务要求
&lt;span class=&quot;hljs-bullet&quot;&gt;1.&lt;/span&gt; 通过API下载并整理 docx 中的文字、图片、发送人和发送时间
&lt;span class=&quot;hljs-bullet&quot;&gt;2.&lt;/span&gt; 识别有效运动打卡，忽略闲聊、排行榜和无关图片
&lt;span class=&quot;hljs-bullet&quot;&gt;3.&lt;/span&gt; 输出运动时间、提交时间、人员、运动类型、运动时长、卡路里、原图路径和核查状态
&lt;span class=&quot;hljs-bullet&quot;&gt;4.&lt;/span&gt; 对重复记录、时间不完整、字段冲突和低置信内容标记为待核查
&lt;span class=&quot;hljs-bullet&quot;&gt;5.&lt;/span&gt; 输出 records.csv 与 evidence.md

处理原则
&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; 不确定时不要猜测，不要写入正式数据
&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; 每条记录必须保留可回看的原图或来源位置
&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; 运动时间和提交时间分开保存
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-id=&quot;heading-3&quot;&gt;核心思路&lt;/h2&gt;
&lt;p&gt;这个流程最核心的工作，就是识别出聊天内容中的运动记录。&lt;/p&gt;
&lt;p&gt;我之前用同一份含 13 张图片的 docx 做过测试。全量视觉识别加大模型理解，识别效果很好，但耗时约 30 分钟，测试成本约 1.63 美元。改成 OCR 先读取文字，再交给大模型整理，同一份资料约 2 分钟，成本约 0.42 美元，不过出现了两处数据错误 。&lt;/p&gt;
&lt;p&gt;对比之下，全部使用AI大模型视觉识别通过率高（但不代表正确率100%），但费用高不可持续；使用OCR识别成本低，但通过率没那么高。事实上做数据的整理如果单纯只做图片识别还是非常容易把数据搞错，因为活动成员做打卡时有可能发送重复app截图，或者直发现场照片加文字描述，并不会有app截图。&lt;/p&gt;
&lt;p&gt;后来我把处理流程再收紧了一步。OCR 负责识别文字、数字。AI 负责理解字段含义和聊天上下文。OCR 缺字、错位、冲突或置信不足时，Agent 先写入待复核清单。人工确认需要补看原图后，才启用大模型视觉识别做二次修订。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/202d3a21e39a4ec0b80a67067930c8cb~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=%2BfgOFBWzK2cJycdEBP1ToOlKH%2Bc%3D&quot; alt=&quot;exec-eeea4bdb-3e7e-4a38-90bc-710e84d1f6eb.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;这套分工解决了两个问题。大部分规则化截图不必都走昂贵的视觉理解，复杂图片也不会因为 OCR 读错一个数字就直接写进结果。&lt;/p&gt;
&lt;p&gt;比如一张截图里 OCR 已经读出“跑步 46 分钟、326 千卡”，AI 可以结合图片版式和同一条消息的上下文，判断哪一个是运动时间，哪一个只是提交时间。若 OCR 把 &lt;code&gt;8:36&lt;/code&gt; 读成了不完整的时间，或卡路里字段没有读出，Agent 会把这一行标成待核查。人工确认需要处理后，才让视觉识别回看原图补齐字段。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-4&quot;&gt;具体操作步骤&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-5&quot;&gt;第一步　让 TRAE Work 下载资料、识别数据并生成 CSV&lt;/h3&gt;
&lt;p&gt;我先在 TRAE Work 中打开 sportcheckin 工程，用自然语言告诉 Agent 要处理的日期范围。Agent 会按指令下载对应的群聊记录，读取聊天文本和运动截图，再结合提交人的上下文整理运动数据。&lt;/p&gt;
&lt;p&gt;处理完成后，Agent 会生成 &lt;code&gt;records.csv&lt;/code&gt;，同时输出识别摘要、来源信息和过滤诊断。CSV 是后续入库和统计的统一输入，每一行仍然能够追溯到原始聊天记录与截图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/72b7917b38fd490c904920b74006164d~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=M6LxRX7HZztnqSf8bsa1W0edrXM%3D&quot; alt=&quot;Pasted image 20260808111841.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/42a18927f9f2407f944efd304b75edb5~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=nmUuCWHKmSCxxEnWmYJTSUxKIS4%3D&quot; alt=&quot;04-trae-review-result.jpg&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-6&quot;&gt;第二步　导入数据库并完成人工核查&lt;/h3&gt;
&lt;p&gt;生成 CSV 后，我把数据导入 sportcheckin 的数据管理系统，这个小小的系统也是vibe coding生成的。系统支持把已核查记录和待核查记录分开，管理员只需要集中处理缺失、冲突和低置信度的数据，不必重新翻完整的群聊记录。&lt;/p&gt;
&lt;p&gt;核查页面会把原图、识别字段和异常原因放在一起。管理员可以对照截图修改运动时间、时长、人员等字段，再确认核查结果，如果有问题可以马上修改。当然如果下载数据和核查数据是同个人，我会建议优先在上一步，下载数据阶段就直接核查了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/e4c923d281ba4a5cb70288190d838f06~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=nWJivSunKkjt2%2BokppcWSBIjN4E%3D&quot; alt=&quot;Pasted image 20260808113958.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/255937ae6ebc4ca5ad02bb06ece63ff0~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=0H3C9I0jG0WzdkZFceuO0YNYZEM%3D&quot; alt=&quot;Pasted image 20260808112834.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-7&quot;&gt;第三步　展示统计结果&lt;/h3&gt;
&lt;p&gt;完成核查后，系统基于数据库中的有效记录生成报表。页面可以查看总体打卡情况，也可以按人员筛选，并通过排行榜快速了解参与情况。后续新增的数据只要经过同一套入库和核查流程，报表就能继续更新。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/8ffa2bf057a64e288531d637004c6721~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=EBuXpe5IGofisfoysen1TEuynn0%3D&quot; alt=&quot;Pasted image 20260808113048.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/de6d0c404e95466986f2978a7825cdc0~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=%2BkAwNwyd9%2FZqscNBcAO3%2Bmz21Rg%3D&quot; alt=&quot;Pasted image 20260808113140.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/fd6d579ab6bc4f10ba9a979f0d5d6060~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=8oJuIhvoB1SSADy7z%2Fo5FheVWMo%3D&quot; alt=&quot;Pasted image 20260808113535.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-8&quot;&gt;第四步　通过智能问数继续分析&lt;/h3&gt;
&lt;p&gt;拿到治理并经过人工确认的数据后，就可以继续做智能问数。问数的入口并不固定。可以把数据库的只读 API、调用方式和字段说明提供给 TREA WORK等agent，让agent按问题获取数据，直接在 对话中完成查询和汇总、提供运动建议。也可以把相同的能力接入钉钉机器人，再把机器人放进运动群。群成员直接提问，就能查看个人运动信息、运动趋势分析。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/86fe27ee6e88458db714957edd557152~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=5A4b5vNXFXdsLDEb7erl4eLrFSo%3D&quot; alt=&quot;Pasted image 20260808115710.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
TREA Work上问数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/5104f171038948fe95da760ece977e29~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=ywt8sFag8h2IdNMMl1rabQXKdXw%3D&quot; alt=&quot;Pasted image 20260808115615.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;
钉钉群里问数&lt;/p&gt;
&lt;h2 data-id=&quot;heading-9&quot;&gt;成效如何&lt;/h2&gt;
&lt;p&gt;目前为止已经创建了1210 条运动记录，累计运动 65588 分钟，约 1093.1 小时。使用这套流程以前，负责这项工作同事需要逐张查看运动截图，再手工填写和汇总表格，每周统计一次大约需要 60 分钟。&lt;/p&gt;
&lt;p&gt;现在由 Agent 先下载资料、识别截图并整理 CSV，人工只处理异常记录和最终核查，每周大约需要 10 到 20 分钟，耗时下降比例|约 67% 到 83%。说实话这个流程也不是一蹴而就的，开发初期也是遇到各种规则意外的问题，导致识别异常，感谢我的同事陪我跑了好几轮的迭代才发识别通过率慢慢提升到了接近100%。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-10&quot;&gt;如何复用&lt;/h2&gt;
&lt;p&gt;运动打卡只是一个输入比较杂的例子，关键方法可以迁移到其他“图片多、规则明确、又需要留痕”的工作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;财务整理发票图片，读取号码、日期、金额和税额，再交给财务确认&lt;/li&gt;
&lt;li&gt;行政整理巡检照片，提取地点、时间和异常描述&lt;/li&gt;
&lt;li&gt;项目组处理物流回单或合同扫描件，先形成结构化台账&lt;/li&gt;
&lt;li&gt;运营团队归档报名表和聊天记录，先把重复、缺失和冲突标出来&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些场景都不适合把 AI 当成自动盖章工具。更稳的做法，是让它先做文件整理、文字读取、字段抽取和异常标记，人把精力放在少量需要判断的地方。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/fcd924d28cef4c29b978e0e9ec6b5352~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAgR3lyYXRl:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786778760&amp;amp;x-signature=0ZeOx%2FGAhWFMVZHn9pB%2FqqnCSJY%3D&quot; alt=&quot;07-reuse-scenarios.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-11&quot;&gt;总结&lt;/h3&gt;
&lt;p&gt;这次实践让我更清楚地看到，AI 能不能进入真实工作，首先取决于人是否理解业务。哪些记录可以采用，哪些数据必须核查，出现缺失和冲突时怎样处理，这些规则需要人来确定。规则说清楚以后，AI 才能稳定地接手下载、识别、整理和统计这些重复工作。&lt;/p&gt;
&lt;p&gt;TRAE Work 给我留下较深印象的地方，是它没有把工作停在一段建议或一份示例代码上。它可以进入已有工程，读取项目约定，调用现成工具，再把处理结果写回文件。整个过程中它确实是个实干助手，我负责说明业务目标、检查结果和修正规则，它负责把中间那些具体而繁琐的步骤跑完。&lt;/p&gt;
&lt;p&gt;运动打卡只是一个很小的场景，却包含了许多真实业务都会遇到的问题。原始资料分散，图片版式不一，识别结果可能出错，最终数据还要能够追溯。把这些问题处理好以后，统计报表和智能问数才能建立在可信的数据上。&lt;/p&gt;</description><link>https://juejin.cn/post/7671324278843588635</link><guid isPermaLink="false">https://juejin.cn/post/7671324278843588635</guid><pubDate>Sat, 08 Aug 2026 07:26:01 GMT</pubDate><author>Gyrate</author><category>人工智能</category><category>VibeCoding</category><category>AI编程</category></item><item><title></title><description></description><link>https://juejin.cn/pin/75488825233521869157671128855915364379</link><guid isPermaLink="false">https://juejin.cn/pin/75488825233521869157671128855915364379</guid><pubDate>Sat, 08 Aug 2026 07:22:02 GMT</pubDate><author>echoVic</author><category>Vibe 编程交流圈</category></item><item><title>解剖 Claude Code：从入口架构逆向工程看 AI 编程工具的信任边界设计</title><description>&lt;p&gt;想了解一个 AI 编程工具的真实水平，有两条路：读它的文档，或者读它的代码。文档写的是意图，代码才是现实，两者之间往往隔着好几轮产品妥协。&lt;/p&gt;
&lt;p&gt;这篇文章走的是第二条路。解剖对象是 Claude Code v2.1.88 的源码（仓库：&lt;code&gt;github.com/wjszxli/claude-code&lt;/code&gt;），分析方法有三种：&lt;strong&gt;架构解构法&lt;/strong&gt;——从入口点反推设计意图；&lt;strong&gt;工程度量法&lt;/strong&gt;——用代码规模和组织方式评估复杂度治理；&lt;strong&gt;对比参照法&lt;/strong&gt;——把它放进 VSCode Copilot、Cursor、GitHub CLI 的坐标系里看取舍。三种方法各解剖一次，一个更完整的图景会自己浮出来。&lt;/p&gt;
&lt;p&gt;但解剖本身不是目的。这篇文章真正想回答的问题是：&lt;strong&gt;当一个工具被赋予了执行命令、读写文件、发起网络请求的能力时，它的设计者如何划定&quot;信任半径&quot;？&lt;/strong&gt; Claude Code 的答案，藏在 643 行入口代码和一条 1778 行的路径检查文件里。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-0&quot;&gt;一、架构解构法：入口文件是一个无法自欺的地方&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-1&quot;&gt;cli.tsx：一个被伪装成入口的路由表&lt;/h3&gt;
&lt;p&gt;打开 &lt;code&gt;src/entrypoints/cli.tsx&lt;/code&gt;，只有 30 行。它的函数注释把设计意图写得很坦白：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/**
 * Bootstrap entrypoint - checks for special flags before loading the full CLI.
 * All imports are dynamic to minimize module evaluation for fast paths.
 * Fast-path for --version has zero imports beyond this file.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个文件做的事只有一件：在加载完整 CLI 之前，先检查 argv 里有没有特殊标志。里面藏着十几条快速路径：&lt;code&gt;--version&lt;/code&gt;、&lt;code&gt;--dump-system-prompt&lt;/code&gt;、&lt;code&gt;--claude-in-chrome-mcp&lt;/code&gt;、&lt;code&gt;--chrome-native-host&lt;/code&gt;、&lt;code&gt;--computer-use-mcp&lt;/code&gt;、&lt;code&gt;--daemon-worker&lt;/code&gt;、&lt;code&gt;remote-control&lt;/code&gt;（桥接模式）、&lt;code&gt;daemon&lt;/code&gt;（常驻监督进程）、&lt;code&gt;ps&lt;/code&gt;/&lt;code&gt;logs&lt;/code&gt;/&lt;code&gt;attach&lt;/code&gt;/&lt;code&gt;kill&lt;/code&gt;（后台会话管理）、&lt;code&gt;new&lt;/code&gt;/&lt;code&gt;list&lt;/code&gt;/&lt;code&gt;reply&lt;/code&gt;（模板任务）、&lt;code&gt;environment-runner&lt;/code&gt;、&lt;code&gt;self-hosted-runner&lt;/code&gt;、&lt;code&gt;--tmux --worktree&lt;/code&gt; 组合，最后才是默认路径——加载 &lt;code&gt;main.js&lt;/code&gt;，进入完整 CLI。&lt;/p&gt;
&lt;p&gt;这个结构透露的信息比表面多。&lt;strong&gt;Claude Code 不是一个程序，而是一组共享同一个二进制的进程家族&lt;/strong&gt;：交互式 REPL、MCP 服务器、远程桥接、常驻守护、无头运行器，全都从同一个 &lt;code&gt;claude&lt;/code&gt; 命令分叉出去。入口文件是这个家族的总开关。&lt;/p&gt;
&lt;p&gt;每条快速路径的实现方式都一样：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// Fast-path for --version/-v: zero module loading needed&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (args.&lt;span class=&quot;hljs-property&quot;&gt;length&lt;/span&gt; === &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; &amp;amp;&amp;amp; (args[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;] === &lt;span class=&quot;hljs-string&quot;&gt;&#39;--version&#39;&lt;/span&gt; || args[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;] === &lt;span class=&quot;hljs-string&quot;&gt;&#39;-v&#39;&lt;/span&gt; || args[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;] === &lt;span class=&quot;hljs-string&quot;&gt;&#39;-V&#39;&lt;/span&gt;)) {
  &lt;span class=&quot;hljs-variable language_&quot;&gt;console&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;log&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;`&lt;span class=&quot;hljs-subst&quot;&gt;${MACRO.VERSION}&lt;/span&gt; (Claude Code)`&lt;/span&gt;);
  &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查版本号不加载任何额外模块，打印完直接返回。其他路径稍重一点，但也都用 &lt;code&gt;await import()&lt;/code&gt; 现场加载，不用不引。对一个 CLI 工具来说，这个选择很务实：用户大量调用发生在脚本和快捷键里，启动开销是按毫秒被感知的。&lt;/p&gt;
&lt;p&gt;文件最顶部还有三件事，排在 &lt;code&gt;main()&lt;/code&gt; 之前：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;../macro-shim&quot;&lt;/span&gt;;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; { feature } &lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&#39;bun:bundle&#39;&lt;/span&gt;;

&lt;span class=&quot;hljs-comment&quot;&gt;// Bugfix for corepack auto-pinning, which adds yarnpkg to peoples&#39; package.jsons&lt;/span&gt;
process.&lt;span class=&quot;hljs-property&quot;&gt;env&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;COREPACK_ENABLE_AUTO_PIN&lt;/span&gt; = &lt;span class=&quot;hljs-string&quot;&gt;&#39;0&#39;&lt;/span&gt;;

&lt;span class=&quot;hljs-comment&quot;&gt;// Set max heap size for child processes in CCR environments (containers have 16GB)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (process.&lt;span class=&quot;hljs-property&quot;&gt;env&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;CLAUDE_CODE_REMOTE&lt;/span&gt; === &lt;span class=&quot;hljs-string&quot;&gt;&#39;true&#39;&lt;/span&gt;) {
  &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; existing = process.&lt;span class=&quot;hljs-property&quot;&gt;env&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;NODE_OPTIONS&lt;/span&gt; || &lt;span class=&quot;hljs-string&quot;&gt;&#39;&#39;&lt;/span&gt;;
  process.&lt;span class=&quot;hljs-property&quot;&gt;env&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;NODE_OPTIONS&lt;/span&gt; = existing ? &lt;span class=&quot;hljs-string&quot;&gt;`&lt;span class=&quot;hljs-subst&quot;&gt;${existing}&lt;/span&gt; --max-old-space-size=8192`&lt;/span&gt; : &lt;span class=&quot;hljs-string&quot;&gt;&#39;--max-old-space-size=8192&#39;&lt;/span&gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;宏填充、Corepack 修复、容器环境的堆内存上限，没有一件是业务逻辑。终端工具的处境就是这样：它会被装进 Docker 容器、CI 流水线、SSH 会话和千奇百怪的本地环境里，启动代码必须先替这些环境扫雷，才轮得到功能登场。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-2&quot;&gt;feature()：编译时就把代码删掉&lt;/h3&gt;
&lt;p&gt;入口文件里反复出现的 &lt;code&gt;feature()&lt;/code&gt; 来自 &lt;code&gt;bun:bundle&lt;/code&gt;，是 Bun 的编译时特性标志。它和运行时的 &lt;code&gt;if (config.xxx)&lt;/code&gt; 有本质区别：&lt;strong&gt;构建产物里根本不包含被关掉的分支&lt;/strong&gt;。以 &lt;code&gt;--dump-system-prompt&lt;/code&gt; 为例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// Fast-path for --dump-system-prompt: output the rendered system prompt and exit.&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// Used by prompt sensitivity evals to extract the system prompt at a specific commit.&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// Ant-only: eliminated from external builds via feature flag.&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;hljs-title function_&quot;&gt;feature&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&#39;DUMP_SYSTEM_PROMPT&#39;&lt;/span&gt;) &amp;amp;&amp;amp; args[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;] === &lt;span class=&quot;hljs-string&quot;&gt;&#39;--dump-system-prompt&#39;&lt;/span&gt;) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注释写得很直白：这条路径给内部的提示词敏感性评估用，外部构建里会被 DCE（死代码消除）整个删掉。全仓库数下来，不同的 &lt;code&gt;feature(&#39;XXX&#39;)&lt;/code&gt; 标志有 &lt;strong&gt;89 个&lt;/strong&gt;。也就是说，Claude Code 的发布策略是&quot;一套源码，多个产物&quot;：内部版本、外部版本、不同客户版本，从同一份代码裁剪出来，裁剪发生在字节层面而不是配置层面。外部用户拿到的二进制里，这些功能连字符串都不存在，反编译也找不到。&lt;/p&gt;
&lt;p&gt;这不是一个&quot;性能优化&quot;级别的决策，而是一个&lt;strong&gt;安全决策&lt;/strong&gt;。运行时开关是&quot;门锁上了但钥匙还在房间里&quot;，编译时裁剪是&quot;房间直接拆了&quot;。内部调试命令、实验性功能、研发专用路径——这些东西留在外部二进制里，哪怕用 &lt;code&gt;if (false)&lt;/code&gt; 包着，也仍然是攻击面：它可以被逆向、被 patch、被环境变量意外激活。编译时删除意味着攻击面在产物层面就归零。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-3&quot;&gt;ABLATION_BASELINE：为什么这段代码必须放在入口&lt;/h3&gt;
&lt;p&gt;入口文件里最值得细读的是这段：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// Harness-science L0 ablation baseline. Inlined here (not init.ts) because&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// BashTool/AgentTool/PowerShellTool capture DISABLE_BACKGROUND_TASKS into&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// module-level consts at import time — init() runs too late. feature() gate&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// DCEs this entire block from external builds.&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;hljs-title function_&quot;&gt;feature&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&#39;ABLATION_BASELINE&#39;&lt;/span&gt;) &amp;amp;&amp;amp; process.&lt;span class=&quot;hljs-property&quot;&gt;env&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;CLAUDE_CODE_ABLATION_BASELINE&lt;/span&gt;) {
  &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; (&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; k &lt;span class=&quot;hljs-keyword&quot;&gt;of&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;&#39;CLAUDE_CODE_SIMPLE&#39;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;CLAUDE_CODE_DISABLE_THINKING&#39;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;DISABLE_INTERLEAVED_THINKING&#39;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;DISABLE_COMPACT&#39;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;DISABLE_AUTO_COMPACT&#39;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;CLAUDE_CODE_DISABLE_AUTO_MEMORY&#39;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;CLAUDE_CODE_DISABLE_BACKGROUND_TASKS&#39;&lt;/span&gt;]) {
    process.&lt;span class=&quot;hljs-property&quot;&gt;env&lt;/span&gt;[k] ??= &lt;span class=&quot;hljs-string&quot;&gt;&#39;1&#39;&lt;/span&gt;;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&quot;Ablation&quot;（消融）是机器学习的实验方法：逐个摘掉组件，看性能掉多少，以此衡量每个组件的贡献。这段代码就是一个内置的 L0 基线开关：设一个环境变量，就把思考、上下文压缩、自动记忆、后台任务全部关掉，让 Claude Code 退化成最朴素的问答循环，作为实验对照组。&lt;/p&gt;
&lt;p&gt;产品代码里内嵌实验对照组，这本身就说明团队在系统地度量&quot;每个智能功能到底值多少&quot;。但更有意思的是注释解释的&lt;strong&gt;位置约束&lt;/strong&gt;：为什么放在 &lt;code&gt;cli.tsx&lt;/code&gt; 而不是 &lt;code&gt;init.ts&lt;/code&gt;？因为 BashTool 等模块在 import 时就把 &lt;code&gt;DISABLE_BACKGROUND_TASKS&lt;/code&gt; 读进了模块级常量，&lt;code&gt;init()&lt;/code&gt; 运行时木已成舟。一个看似随意的代码位置，背后是对 ES 模块求值顺序的精确计算。读这种注释，比读任何设计文档都更能看清一个团队的真实水平——&lt;strong&gt;他们理解自己的运行时，精确到模块加载的顺序&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-4&quot;&gt;init.ts：启动决策链就是信任决策链&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;main()&lt;/code&gt; 进入完整 CLI 后，真正的初始化在 &lt;code&gt;src/entrypoints/init.ts&lt;/code&gt;，57 行，被 &lt;code&gt;memoize&lt;/code&gt; 包裹保证只执行一次。它的步骤顺序值得逐步看，因为顺序本身就是设计：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; init = &lt;span class=&quot;hljs-title function_&quot;&gt;memoize&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;async&lt;/span&gt; (): &lt;span class=&quot;hljs-title class_&quot;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-built_in&quot;&gt;void&lt;/span&gt;&amp;gt; =&amp;gt; {
  &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt; {
    &lt;span class=&quot;hljs-title function_&quot;&gt;enableConfigs&lt;/span&gt;()
    &lt;span class=&quot;hljs-comment&quot;&gt;// Apply only safe environment variables before trust dialog&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// Full environment variables are applied after trust is established&lt;/span&gt;
    &lt;span class=&quot;hljs-title function_&quot;&gt;applySafeConfigEnvironmentVariables&lt;/span&gt;()
    &lt;span class=&quot;hljs-comment&quot;&gt;// Apply NODE_EXTRA_CA_CERTS from settings.json to process.env early,&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// before any TLS connections. Bun caches the TLS cert store at boot&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// via BoringSSL, so this must happen before the first TLS handshake.&lt;/span&gt;
    &lt;span class=&quot;hljs-title function_&quot;&gt;applyExtraCACertsFromConfig&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一步是配置系统，但紧接着的动作很有讲究：&lt;strong&gt;先只应用&quot;安全&quot;的环境变量，完整环境变量要等用户通过信任对话框之后才应用&lt;/strong&gt;。CA 证书配置则要赶在第一次 TLS 握手之前，因为 Bun 启动时就用 BoringSSL 缓存了证书库，错过时机再设就没用了。三步操作，一步是安全分区，一步是运行时约束。&lt;/p&gt;
&lt;p&gt;往下是网络层的准备：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;    &lt;span class=&quot;hljs-title function_&quot;&gt;configureGlobalMTLS&lt;/span&gt;()
    &lt;span class=&quot;hljs-title function_&quot;&gt;configureGlobalAgents&lt;/span&gt;()
    &lt;span class=&quot;hljs-comment&quot;&gt;// Preconnect to the Anthropic API — overlap TCP+TLS handshake&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// (~100-200ms) with the ~100ms of action-handler work before the API&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// request.&lt;/span&gt;
    &lt;span class=&quot;hljs-title function_&quot;&gt;preconnectAnthropicApi&lt;/span&gt;()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配好 mTLS 和代理之后，趁命令处理器还在做大约 100ms 的准备工作，提前对 Anthropic API 发起 TCP+TLS 握手，把 100~200ms 的网络建联藏进这段空档里。注释还补了一句：走代理、mTLS 或云厂商网关时跳过预热，因为 SDK 的 dispatcher 在那些情况下复用不了全局连接池。优化做到这个颗粒度，前提是对自己运行环境的每一种变体都摸过底。&lt;/p&gt;
&lt;p&gt;init.ts 里还有一类容易被略过的代码：失败处理。以 CCR 环境的上游代理为例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (&lt;span class=&quot;hljs-title function_&quot;&gt;isEnvTruthy&lt;/span&gt;(process.&lt;span class=&quot;hljs-property&quot;&gt;env&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;CLAUDE_CODE_REMOTE&lt;/span&gt;)) {
      &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt; {
        &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; { initUpstreamProxy, getUpstreamProxyEnv } = &lt;span class=&quot;hljs-keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt;(
          &lt;span class=&quot;hljs-string&quot;&gt;&#39;../upstreamproxy/upstreamproxy.js&#39;&lt;/span&gt;
        )
        &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;initUpstreamProxy&lt;/span&gt;()
      } &lt;span class=&quot;hljs-keyword&quot;&gt;catch&lt;/span&gt; (err) {
        &lt;span class=&quot;hljs-title function_&quot;&gt;logForDebugging&lt;/span&gt;(
          &lt;span class=&quot;hljs-string&quot;&gt;`[init] upstreamproxy init failed: ...; continuing without proxy`&lt;/span&gt;,
          { &lt;span class=&quot;hljs-attr&quot;&gt;level&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&#39;warn&#39;&lt;/span&gt; },
        )
      }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代理初始化失败，记一条 warn 日志，继续启动。这叫 &lt;strong&gt;fail-open&lt;/strong&gt;：代理是增强项不是命脉，它挂了不该拖死整个工具。启动路径上哪些组件允许 fail-open、哪些必须 fail-closed（比如配置解析失败会直接弹错误对话框），是一个工具成熟度的直接体现。&lt;/p&gt;
&lt;p&gt;最后是遥测。遥测的初始化不在 &lt;code&gt;init()&lt;/code&gt; 里，而是单独一个函数：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/**
 * Initialize telemetry after trust has been granted.
 * ...
 * This should only be called once, after the trust dialog has been accepted.
 */&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;initializeTelemetryAfterTrust&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;/span&gt;): &lt;span class=&quot;hljs-built_in&quot;&gt;void&lt;/span&gt; {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;遥测等信任确立之后才启动&lt;/strong&gt;，而且实现上把 OpenTelemetry 加 protobuf 约 400KB 的模块延迟到真正启用时才加载，gRPC 导出器的约 700KB 再往后延。注释里连字节数都标了，说明这些延迟是算过账的，不是顺手为之。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这里藏着一个值得单拎出来的设计哲学：信任不是全有全无的开关，而是一个分阶段建立的过程。&lt;/strong&gt; 第一阶段，进程启动，只应用&quot;安全&quot;的环境变量——这些变量即使被恶意注入也不会造成损害；第二阶段，用户通过信任对话框，建立对工作目录的信任；第三阶段，信任确立后，才应用完整环境变量、启动遥测。每一个阶段对应一个信任层级，每一层授予的能力恰好匹配该层级的风险敞口。这就是&quot;安全分区&quot;（security partitioning）在启动序列里的具体实现。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-5&quot;&gt;从入口层能确认的几件事&lt;/h3&gt;
&lt;p&gt;把 cli.tsx 和 init.ts 合起来，能确认的工程事实如下：&lt;/p&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;代码证据&lt;/th&gt;&lt;th&gt;对应的工程决策&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;--version&lt;/code&gt; 零导入返回&lt;/td&gt;&lt;td&gt;高频轻操作不触发全量初始化&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;十几条快速路径共用入口&lt;/td&gt;&lt;td&gt;单二进制多进程角色，按需分叉&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;89 个 &lt;code&gt;feature()&lt;/code&gt; 编译时标志&lt;/td&gt;&lt;td&gt;一套源码裁剪出多个发布产物&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;消融基线内嵌在入口&lt;/td&gt;&lt;td&gt;智能功能逐个接受实验度量&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;安全环境变量先于信任对话框&lt;/td&gt;&lt;td&gt;信任边界切进启动顺序&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;遥测在信任之后、延迟 400KB+&lt;/td&gt;&lt;td&gt;隐私承诺和启动性能一起落地&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;API 预连接复用 100ms 空档&lt;/td&gt;&lt;td&gt;网络建联藏进准备工作的间隙&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;上游代理 fail-open&lt;/td&gt;&lt;td&gt;增强组件不许拖死主流程&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;这八条里没有一条来自官方宣传，全是入口层 643 行代码里的实际行为。这也是&quot;从入口读起&quot;这个方法的价值：&lt;strong&gt;入口文件是一个系统里少有的、无法自欺的地方&lt;/strong&gt;。所有路径从这里出发，所有环境假设在这里摊开。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-6&quot;&gt;二、工程度量法：复杂度治理的六个侧面&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-7&quot;&gt;目录拓扑：35 个一级目录的分工&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/&lt;/code&gt; 下有 35 个一级目录。挑重点的几个：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;src/
├── entrypoints/    &lt;span class=&quot;hljs-comment&quot;&gt;# 4 个入口文件 + SDK 子目录&lt;/span&gt;
├── commands/       &lt;span class=&quot;hljs-comment&quot;&gt;# 88 个子目录 + 15 个文件，用户侧命令&lt;/span&gt;
├── tools/          &lt;span class=&quot;hljs-comment&quot;&gt;# 46 个子目录，智能体可调用的工具&lt;/span&gt;
├── services/       &lt;span class=&quot;hljs-comment&quot;&gt;# 21 个子目录 + 16 个文件，业务服务层&lt;/span&gt;
├── components/     &lt;span class=&quot;hljs-comment&quot;&gt;# 31 个子目录 + 113 个文件，Ink TUI 组件&lt;/span&gt;
├── utils/          &lt;span class=&quot;hljs-comment&quot;&gt;# 31 个子目录 + 299 个文件，最大的目录（约 18 万行）&lt;/span&gt;
├── hooks/          &lt;span class=&quot;hljs-comment&quot;&gt;# 83 个文件，生命周期钩子&lt;/span&gt;
├── ink/            &lt;span class=&quot;hljs-comment&quot;&gt;# 自研终端渲染引擎（约 2 万行）&lt;/span&gt;
├── bridge/         &lt;span class=&quot;hljs-comment&quot;&gt;# 远程桥接模式&lt;/span&gt;
├── coordinator/    &lt;span class=&quot;hljs-comment&quot;&gt;# 多代理协调&lt;/span&gt;
├── tasks/          &lt;span class=&quot;hljs-comment&quot;&gt;# 后台任务系统&lt;/span&gt;
├── voice/          &lt;span class=&quot;hljs-comment&quot;&gt;# 语音模式&lt;/span&gt;
├── skills/         &lt;span class=&quot;hljs-comment&quot;&gt;# 技能加载与解析&lt;/span&gt;
├── plugins/        &lt;span class=&quot;hljs-comment&quot;&gt;# 插件系统&lt;/span&gt;
└── ...             &lt;span class=&quot;hljs-comment&quot;&gt;# state / schemas / types / vim / memdir 等&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;几个数字值得注意。&lt;code&gt;commands/&lt;/code&gt; 和 &lt;code&gt;tools/&lt;/code&gt; 是两组不同的概念：88 个命令目录面向用户（&lt;code&gt;claude config&lt;/code&gt;、&lt;code&gt;claude diff&lt;/code&gt;……），46 个工具目录面向模型（&lt;code&gt;FileReadTool&lt;/code&gt;、&lt;code&gt;BashTool&lt;/code&gt;、&lt;code&gt;WebSearchTool&lt;/code&gt;……）。&lt;strong&gt;人走前门，AI 走后门，两个入口各自演化。&lt;/strong&gt; 一条命令可以触发多个工具，一个工具也会被多条命令和纯对话路径复用。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ink/&lt;/code&gt; 目录单列值得强调：Claude Code 没有用第三方的 Ink（React for CLI），而是自己维护了一套约 2 万行的终端渲染引擎。&lt;code&gt;bridge/&lt;/code&gt;、&lt;code&gt;coordinator/&lt;/code&gt;、&lt;code&gt;voice/&lt;/code&gt; 这些目录则说明它早已超出&quot;终端聊天工具&quot;的范畴，覆盖了远程控制、多代理、多模态交互——叫它&quot;终端智能体平台&quot;更准确。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-8&quot;&gt;大文件分布：复杂度淤积在接缝处&lt;/h3&gt;
&lt;p&gt;按行数排出最大的七个文件：&lt;/p&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;文件&lt;/th&gt;&lt;th&gt;行数&lt;/th&gt;&lt;th&gt;职责&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;cli/print.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;5594&lt;/td&gt;&lt;td&gt;非交互输出管道&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;utils/messages.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;5512&lt;/td&gt;&lt;td&gt;消息类型与转换&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;utils/sessionStorage.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;5105&lt;/td&gt;&lt;td&gt;会话持久化&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;utils/hooks.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;5022&lt;/td&gt;&lt;td&gt;钩子编排&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;screens/REPL.tsx&lt;/code&gt;&lt;/td&gt;&lt;td&gt;5005&lt;/td&gt;&lt;td&gt;交互主界面&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;main.tsx&lt;/code&gt;&lt;/td&gt;&lt;td&gt;4684&lt;/td&gt;&lt;td&gt;完整 CLI 装配&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;utils/bash/bashParser.ts&lt;/code&gt;&lt;/td&gt;&lt;td&gt;4436&lt;/td&gt;&lt;td&gt;Bash 命令解析&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;看这份榜单比看平均数有用。&lt;strong&gt;最大的文件没有一个是&quot;业务功能&quot;，全部是接缝层&lt;/strong&gt;：渲染管道、消息协议、会话存储、界面装配、shell 语法解析。这是大代码库的典型淤积规律——边界翻译层的复杂度随两边系统的演化持续增长，又很难拆分，因为拆开后每一半都得带着对方的上下文才能看懂。&lt;code&gt;bashParser.ts&lt;/code&gt; 尤其说明问题：为了安全地判断一条 bash 命令想干什么，Claude Code 自己实现了 4400 行的 shell 语法解析器。这个投入的回报，后面讲权限系统时还会算到。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-9&quot;&gt;commands.ts：755 行的命令装配车间&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/commands.ts&lt;/code&gt; 负责把所有命令来源装配成一个列表。源码里能清楚看到三层机制：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// 第一层：编译时裁剪。feature() 为 false 时，对应 require 整段消失&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; proactive = &lt;span class=&quot;hljs-title function_&quot;&gt;feature&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&#39;PROACTIVE&#39;&lt;/span&gt;) || &lt;span class=&quot;hljs-title function_&quot;&gt;feature&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&#39;KAIROS&#39;&lt;/span&gt;)
  ? &lt;span class=&quot;hljs-built_in&quot;&gt;require&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&#39;./commands/proactive.js&#39;&lt;/span&gt;).&lt;span class=&quot;hljs-property&quot;&gt;default&lt;/span&gt;
  : &lt;span class=&quot;hljs-literal&quot;&gt;null&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;// 第二层：内部命令，仅 USER_TYPE === &#39;ant&#39; 的构建可见&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;hljs-variable constant_&quot;&gt;INTERNAL_ONLY_COMMANDS&lt;/span&gt; = [
  backfillSessions, breakCache, bughunter, ...
]

&lt;span class=&quot;hljs-comment&quot;&gt;// 第三层：磁盘命令源，并行加载，按 cwd 记忆化&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; loadAllCommands = &lt;span class=&quot;hljs-title function_&quot;&gt;memoize&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;async&lt;/span&gt; (&lt;span class=&quot;hljs-attr&quot;&gt;cwd&lt;/span&gt;: &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;): &lt;span class=&quot;hljs-title class_&quot;&gt;Promise&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;Command&lt;/span&gt;[]&amp;gt; =&amp;gt; {
  &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; [
    { skillDirCommands, pluginSkills, bundledSkills, builtinPluginSkills },
    pluginCommands,
    workflowCommands,
  ] = &lt;span class=&quot;hljs-keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Promise&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;all&lt;/span&gt;([
    &lt;span class=&quot;hljs-title function_&quot;&gt;getSkills&lt;/span&gt;(cwd),
    &lt;span class=&quot;hljs-title function_&quot;&gt;getPluginCommands&lt;/span&gt;(),
    getWorkflowCommands ? &lt;span class=&quot;hljs-title function_&quot;&gt;getWorkflowCommands&lt;/span&gt;(cwd) : &lt;span class=&quot;hljs-title class_&quot;&gt;Promise&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;resolve&lt;/span&gt;([]),
  ])
  &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; [
    ...bundledSkills,
    ...builtinPluginSkills,
    ...skillDirCommands,
    ...workflowCommands,
    ...pluginCommands,
    ...pluginSkills,
    ...&lt;span class=&quot;hljs-title function_&quot;&gt;COMMANDS&lt;/span&gt;(),
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;三层各管一件事。&lt;code&gt;feature()&lt;/code&gt; 在编译时把实验性命令从产物里删掉；&lt;code&gt;INTERNAL_ONLY_COMMANDS&lt;/code&gt; 把研发调试命令隔离在内部构建里，外部用户连名字都看不到；&lt;code&gt;memoize&lt;/code&gt; + &lt;code&gt;Promise.all&lt;/code&gt; 则处理运行时成本——技能、插件、工作流都要读磁盘，缓存到 cwd 级别，并行加载。&lt;/p&gt;
&lt;p&gt;命令列表上方还有一段注释值得引：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/**
 * Returns commands available to the current user. The expensive loading is
 * memoized, but availability and isEnabled checks run fresh every call so
 * auth changes (e.g. /login) take effect immediately.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;加载可以缓存，权限判断必须每次重算&lt;/strong&gt;，因为用户可能中途 &lt;code&gt;/login&lt;/code&gt;。性能和正确性的分界线划在哪里，这段注释交代得很清楚。命令空间因此能同时容纳内置命令、技能命令、插件命令、工作流命令和内部命令五种租户，各走各的可见性规则。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-10&quot;&gt;Tool 接口：46 个工具共用一套契约，默认不信任&lt;/h3&gt;
&lt;p&gt;46 个工具目录能并行生长，靠的是 &lt;code&gt;src/Tool.ts&lt;/code&gt; 里的统一接口。它的几个关键字段：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;inputSchema&lt;/span&gt;: &lt;span class=&quot;hljs-title class_&quot;&gt;Input&lt;/span&gt;                        &lt;span class=&quot;hljs-comment&quot;&gt;// Zod 校验模式&lt;/span&gt;
&lt;span class=&quot;hljs-title function_&quot;&gt;isConcurrencySafe&lt;/span&gt;(&lt;span class=&quot;hljs-attr&quot;&gt;input&lt;/span&gt;: z.&lt;span class=&quot;hljs-property&quot;&gt;infer&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;Input&lt;/span&gt;&amp;gt;): &lt;span class=&quot;hljs-built_in&quot;&gt;boolean&lt;/span&gt;  &lt;span class=&quot;hljs-comment&quot;&gt;// 能否与其他工具并行&lt;/span&gt;
&lt;span class=&quot;hljs-title function_&quot;&gt;isReadOnly&lt;/span&gt;(&lt;span class=&quot;hljs-attr&quot;&gt;input&lt;/span&gt;: z.&lt;span class=&quot;hljs-property&quot;&gt;infer&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;Input&lt;/span&gt;&amp;gt;): &lt;span class=&quot;hljs-built_in&quot;&gt;boolean&lt;/span&gt;         &lt;span class=&quot;hljs-comment&quot;&gt;// 是否只读&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接口设计里最妙的是默认值。源码里 &lt;code&gt;buildTool()&lt;/code&gt; 的注释写明：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt; * - &lt;span class=&quot;hljs-string&quot;&gt;`isConcurrencySafe`&lt;/span&gt; → &lt;span class=&quot;hljs-string&quot;&gt;`false`&lt;/span&gt; (assume not safe)
 * - &lt;span class=&quot;hljs-string&quot;&gt;`isReadOnly`&lt;/span&gt; → &lt;span class=&quot;hljs-string&quot;&gt;`false`&lt;/span&gt; (assume writes)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;不声明并发安全就按不安全处理，不声明只读就按会写处理。默认不信任，声明才放行。&lt;/strong&gt; 这让工具作者想偷懒的代价是失去并行调度的资格，而不是获得它。方向对了，46 个工具自治生长才不会变成安全债。&lt;/p&gt;
&lt;p&gt;这个&quot;默认不信任&quot;（default-deny）模式值得单独展开。在传统插件生态里，常见的做法是&quot;默认放行，遇到问题再收紧&quot;——结果就是生态越繁荣，安全水位越低。而 Claude Code 把默认值设成最保守的那个：你什么都没说，我就当你既不能并行、又可能写入。代价是工具作者必须多写两行声明，收益是整个调度器和权限系统可以信任&quot;未声明 = 不安全&quot;这个前提。&lt;strong&gt;在一个 AI 工具里，这个前提尤其重要&lt;/strong&gt;：因为工具的调用者不是人类而是模型，模型不会像人一样&quot;看情况&quot;，它只会严格按照声明行事。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-11&quot;&gt;BashTool/prompt.ts：369 行的工具说明书&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/tools/BashTool/prompt.ts&lt;/code&gt; 有 369 行，主体不是逻辑，是给模型看的操作手册：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;getSimplePrompt&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;/span&gt;): &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; {
  &lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; toolPreferenceItems = [
    &lt;span class=&quot;hljs-string&quot;&gt;`File search: Use &lt;span class=&quot;hljs-subst&quot;&gt;${GLOB_TOOL_NAME}&lt;/span&gt; (NOT find or ls)`&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;`Content search: Use &lt;span class=&quot;hljs-subst&quot;&gt;${GREP_TOOL_NAME}&lt;/span&gt; (NOT grep or rg)`&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;`Read files: Use &lt;span class=&quot;hljs-subst&quot;&gt;${FILE_READ_TOOL_NAME}&lt;/span&gt; (NOT cat/head/tail)`&lt;/span&gt;,
    &lt;span class=&quot;hljs-string&quot;&gt;`Edit files: Use &lt;span class=&quot;hljs-subst&quot;&gt;${FILE_EDIT_TOOL_NAME}&lt;/span&gt; (NOT sed/awk)`&lt;/span&gt;,
    &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;
  ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;手册细致到什么程度？git 协议里写明 &lt;code&gt;NEVER skip hooks (--no-verify, --no-gpg-sign, etc)&lt;/code&gt;；解释为什么不让模型用 &lt;code&gt;find -regex&lt;/code&gt; 时，连正则引擎的差异都讲清楚了：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// bfs (which backs `find`) uses Oniguruma for -regex, which picks the&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// FIRST matching alternative (leftmost-first), unlike GNU find&#39;s&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// POSIX leftmost-longest. This silently drops matches when a shorter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oniguruma 的 leftmost-first 和 POSIX 的 leftmost-longest 会导致匹配结果不同，这种坑人类工程师都未必记得住，团队把它写进提示词防模型踩雷。还有一点容易被忽略：&lt;strong&gt;这些提示词是 TypeScript 代码，工具名用常量插值，改一次工具名全仓库联动&lt;/strong&gt;。提示词在这里不是贴在配置里的文本，而是走类型检查、代码审查和版本控制的工程产物——把提示词当代码管理，是这个代码库最值得借鉴的作法之一。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-12&quot;&gt;filesystem.ts：1778 行的路径攻防&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;src/utils/permissions/filesystem.ts&lt;/code&gt; 有 1778 行，只回答一个问题：&lt;strong&gt;AI 要碰某个文件路径时，放行、拒绝还是问用户？&lt;/strong&gt; 一个&quot;路径检查&quot;写到这个体量，是因为它面对的是对抗性输入。看它的检查清单：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/**
 * Detects suspicious Windows path patterns that could bypass security checks.
 * - NTFS Alternate Data Streams (e.g., file.txt::$DATA or file.txt:stream)
 * - 8.3 short names (e.g., GIT~1, CLAUDE~1, SETTIN~1.JSON)
 * - Long path prefixes (e.g., \\?\C:\..., \\.\C:\...)
 * - Trailing dots and spaces (e.g., .git., .claude , .bashrc...)
 * - DOS device names (e.g., .git.CON, settings.json.PRN)
 * - Three or more consecutive dots (e.g., .../file.txt)
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ADS 备用数据流、8.3 短名、长路径前缀、尾部点号、DOS 设备名，每一类都是真实存在的绕过手法。注释还专门解释了为什么在非 Windows 平台也要查：NTFS 可以挂载到 Linux 和 macOS 上（ntfs-3g），同样的绕过照样成立。&lt;/p&gt;
&lt;p&gt;更见功力的是它对&quot;为什么不直接规范化&quot;的论证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-typescript&quot; lang=&quot;typescript&quot;&gt; * &lt;span class=&quot;hljs-title class_&quot;&gt;An&lt;/span&gt; alternative approach would be to normalize these paths using &lt;span class=&quot;hljs-title class_&quot;&gt;Windows&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;APIs&lt;/span&gt;
 * (e.&lt;span class=&quot;hljs-property&quot;&gt;g&lt;/span&gt;., &lt;span class=&quot;hljs-title class_&quot;&gt;GetLongPathNameW&lt;/span&gt;). &lt;span class=&quot;hljs-title class_&quot;&gt;However&lt;/span&gt;, &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt; approach has significant &lt;span class=&quot;hljs-attr&quot;&gt;challenges&lt;/span&gt;:
 * &lt;span class=&quot;hljs-number&quot;&gt;1.&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Filesystem&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;dependency&lt;/span&gt;: ... files that don&lt;span class=&quot;hljs-string&quot;&gt;&#39;t exist yet cannot be normalized.
 * 2. Race conditions: ... TOCTOU (Time-Of-Check-Time-Of-Use) vulnerabilities.
 * 3. Complexity: ...
 * 4. Reliability: Pattern detection is more predictable ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;调系统 API 规范化路径听起来更&quot;正统&quot;，但新文件还没存在无法规范化，检查和使用的间隙文件系统可能变化（TOCTOU 竞争）。团队选择只做模式检测、命中即转人工审批，理由是行为可预测。&lt;strong&gt;这是在充分理解替代方案的缺陷之后做的减法，比&quot;多加几层检查&quot;难得多。&lt;/strong&gt; 这 1778 行里最值钱的不是检查项本身，而是&quot;知道什么不该做&quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-13&quot;&gt;三、对比参照法：三种路线，一种演化方向&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-14&quot;&gt;参照系一：VSCode Copilot——扩展路线&lt;/h3&gt;
&lt;p&gt;Copilot 作为 VSCode 插件存在，架构是寄生式的：它用独立的 Node.js 进程跑语言服务器，通过 LSP 和宿主通信；&lt;code&gt;src/platform&lt;/code&gt; 抽象 VSCode API，&lt;code&gt;instantiationService&lt;/code&gt; 用依赖注入管理服务生命周期。工程上很成熟，但&lt;strong&gt;能力天花板由宿主决定&lt;/strong&gt;：扩展能做的事，是 VSCode 扩展 API 的子集。&lt;/p&gt;
&lt;p&gt;Claude Code 的能力边界是操作系统：46 个工具可以直接执行命令、读写文件、发起网络请求。两者的差距不是功能多少，而是假设不同。Copilot 假设开发发生在 IDE 里；Claude Code 假设开发可能发生在任何有 shell 的地方——SSH 会话、CI 管道、容器、远程服务器。代价也很直接：Copilot 不用操心终端渲染、不用自建权限系统，这些 Claude Code 全得自己扛，前面那 2 万行 ink 和 1778 行 filesystem.ts 就是账单的明细。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-15&quot;&gt;参照系二：Cursor——IDE 原生路线&lt;/h3&gt;
&lt;p&gt;Cursor 比 Copilot 激进：直接 fork VSCode，把 AI 做进编辑器内核。它对整个仓库建语义索引，能回答全局性问题；补全走自研小模型保延迟，复杂推理走 GPT 系大模型；后台 Agent 可以在用户看不见的地方跑长任务。在&quot;IDE 内的智能密度&quot;这个方向上，Cursor 做到了当前形态的极致。&lt;/p&gt;
&lt;p&gt;两个产品与其说是竞争，不如说是各自占住一个场景。本地写复杂前端项目，Cursor 的图形交互和索引能力更顺手；登上无头服务器排障，Cursor 够不着，Claude Code 可以。值得注意的是两边正在向对方的地盘伸手：Cursor 做了后台 Agent，Claude Code 有 &lt;code&gt;bridge/&lt;/code&gt; 远程模式和后台任务系统。&lt;strong&gt;长任务在后台跑、完成再汇报，这个交互模型正在跨形态趋同&lt;/strong&gt;，将来分胜负的可能不是&quot;IDE 还是终端&quot;，而是谁的工具编排和权限模型更能撑住无人值守的执行。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-16&quot;&gt;参照系三：GitHub CLI——纯 CLI 路线&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;gh&lt;/code&gt; 把 GitHub 的 Web 功能搬进终端，Go 实现，是传统 CLI 工程的样板：Cobra 做命令分层，&lt;code&gt;cmdutil.Factory&lt;/code&gt; 做依赖注入，退出码有严格语义（0 成功、1 一般错误、2 取消、4 认证错误、8 挂起），第三方可以按目录约定扩展子命令。&lt;code&gt;gh&lt;/code&gt; 是命令驱动的：用户知道自己要做什么，工具负责最短路径执行。&lt;/p&gt;
&lt;p&gt;Claude Code 是意图驱动的：用户描述目标，模型决定调哪些工具。这是两代 CLI 的分界。但在实现层，Claude Code 大量继承了 &lt;code&gt;gh&lt;/code&gt; 这一代 CLI 的家底：&lt;code&gt;commands/&lt;/code&gt; 目录对应 Cobra 的命令树，&lt;code&gt;services/&lt;/code&gt; 对应工厂层的标准化服务，技能和插件机制对应扩展系统。说它是&quot;CLI 工程最佳实践之上叠了一层智能体编排&quot;并不夸张。反过来，它也有没继承好的地方：退出码语义、机器可读输出这类脚本友好性，就不是它的设计重心——&lt;code&gt;cli/print.ts&lt;/code&gt; 那 5594 行正是在补这块短板。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-17&quot;&gt;四个坐标的对照&lt;/h3&gt;




































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;维度&lt;/th&gt;&lt;th&gt;VSCode Copilot&lt;/th&gt;&lt;th&gt;Cursor&lt;/th&gt;&lt;th&gt;GitHub CLI&lt;/th&gt;&lt;th&gt;Claude Code&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;宿主策略&lt;/td&gt;&lt;td&gt;IDE 扩展&lt;/td&gt;&lt;td&gt;IDE 原生&lt;/td&gt;&lt;td&gt;独立 CLI&lt;/td&gt;&lt;td&gt;独立 CLI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AI 深度&lt;/td&gt;&lt;td&gt;补全 + 聊天&lt;/td&gt;&lt;td&gt;全局索引 + 推理&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;智能体编排&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;上下文范围&lt;/td&gt;&lt;td&gt;当前文件及邻域&lt;/td&gt;&lt;td&gt;整个代码库&lt;/td&gt;&lt;td&gt;命令参数&lt;/td&gt;&lt;td&gt;工作目录，可扩展&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;交互范式&lt;/td&gt;&lt;td&gt;图形侧边栏&lt;/td&gt;&lt;td&gt;图形嵌入式&lt;/td&gt;&lt;td&gt;命令式&lt;/td&gt;&lt;td&gt;对话式 + 命令式&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;运行环境&lt;/td&gt;&lt;td&gt;桌面 IDE&lt;/td&gt;&lt;td&gt;桌面 IDE&lt;/td&gt;&lt;td&gt;任何终端&lt;/td&gt;&lt;td&gt;任何终端&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;能力边界&lt;/td&gt;&lt;td&gt;IDE 扩展 API&lt;/td&gt;&lt;td&gt;IDE 内核 + 云服务&lt;/td&gt;&lt;td&gt;操作系统命令&lt;/td&gt;&lt;td&gt;操作系统 + 网络 + MCP&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;用户假设&lt;/td&gt;&lt;td&gt;开发者在 IDE 内&lt;/td&gt;&lt;td&gt;开发者在 IDE 内&lt;/td&gt;&lt;td&gt;用户明确知道命令&lt;/td&gt;&lt;td&gt;用户描述意图，模型选路径&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;架构重心&lt;/td&gt;&lt;td&gt;API 抽象层&lt;/td&gt;&lt;td&gt;索引与推理&lt;/td&gt;&lt;td&gt;命令分发&lt;/td&gt;&lt;td&gt;工具编排与权限&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;表格里最值得停留的是最后一行。Copilot 的重心是抽象层，Cursor 的重心是索引和推理，&lt;code&gt;gh&lt;/code&gt; 的重心是命令分发，而 Claude Code 把最大的工程投入压在了工具编排和权限上——46 个工具的契约、1778 行的路径检查、4436 行的 bash 解析器，全是这条重心的注脚。&lt;strong&gt;这个分配方式回答了一个产品问题：当模型能力由 API 提供商决定、各家拉不开差距时，差异就沉淀在&quot;模型能安全地够到多少东西&quot;上。&lt;/strong&gt; Claude Code 赌的是这个。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-18&quot;&gt;四、深层启示：信任半径、接缝淤积与智能体的安全底座&lt;/h2&gt;
&lt;p&gt;三种方法走完，把整张逻辑图拼起来，可以得到六条可迁移的启示。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-19&quot;&gt;启示一：把启动序列当作一条有安全分区的流水线&lt;/h3&gt;
&lt;p&gt;Claude Code 的 init.ts 里每一步都有明确的前提：CA 证书必须在第一次 TLS 握手前，安全环境变量必须在信任对话框前，遥测必须在信任确立后。&lt;strong&gt;初始化顺序不是细节，是架构。&lt;/strong&gt; 借鉴到实践中：给自己系统的初始化代码画一张顺序图，给每一步标注&quot;它依赖什么、谁依赖它&quot;。凡是说不清前提的步骤，都是将来事故的埋点。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-20&quot;&gt;启示二：特性裁剪尽量发生在编译时&lt;/h3&gt;
&lt;p&gt;运行时的 &lt;code&gt;if (enabled)&lt;/code&gt; 只是把门关上，编译时的 &lt;code&gt;feature()&lt;/code&gt; 是把房间拆掉——内部调试命令、实验功能在外部产物里连字符串都不存在。如果你的产品需要区分社区版、商业版、内部版，裁剪层级越早，泄密面和包体积越小，审计成本也越低。&lt;strong&gt;在 AI 工具这个品类里，&quot;用户拿不到的东西&quot;比&quot;用户用不了的东西&quot;安全得多&lt;/strong&gt;——因为攻击者可以通过 patch 二进制、注入环境变量、hook 运行时来重新打开那扇门，但没法让一个不存在的房间重新出现。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-21&quot;&gt;启示三：扩展接口的默认值决定生态的安全水位&lt;/h3&gt;
&lt;p&gt;Tool 接口里 &lt;code&gt;isConcurrencySafe&lt;/code&gt; 和 &lt;code&gt;isReadOnly&lt;/code&gt; 默认都是 false：不声明就按最坏情况处理，声明了才获得调度优待。&lt;strong&gt;默认放行，生态会朝着粗放生长；默认收紧，作者想偷懒的代价是功能降级而不是安全事故。&lt;/strong&gt; 设计扩展点的时候，先想清楚&quot;不填会怎样&quot;，那才是真正的默认行为。这个原则的适用范围远超插件系统——任何有第三方接入点的系统都适用。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-22&quot;&gt;启示四：把提示词当代码管理&lt;/h3&gt;
&lt;p&gt;提示词写在 TypeScript 里、用常量插值、过类型检查和代码审查，改一次工具名全仓库联动。反例我们都见过：提示词散落在 YAML、数据库和聊天记录里，没人知道线上跑的是哪一版。&lt;strong&gt;只要提示词开始影响产品行为，它就值得享受和业务代码同等的工程待遇&lt;/strong&gt;——版本化、可评审、可回滚。对 AI 产品尤其如此：提示词就是产品逻辑本身，不是&quot;配置&quot;。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-23&quot;&gt;启示五：复杂度会淤积在接缝处&lt;/h3&gt;
&lt;p&gt;这份代码库里最大的文件全是边界翻译层：输出管道、消息协议、会话存储、shell 语法解析。这不是 Claude Code 独有的病，任何系统的两端各自演化，中间的翻译层就会持续增重。实践含义有两个：排期时给接缝层留足余量，它一定超预期；评审时对接缝层的大文件宽容一些，它们大不等于烂——&lt;strong&gt;能在一个地方看全两端的上下文，有时候比拆成十个&quot;干净&quot;的小文件更值钱&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-24&quot;&gt;启示六：智能体产品的差异，沉淀在&quot;模型能安全够到多少东西&quot;上&lt;/h3&gt;
&lt;p&gt;模型能力由 API 提供商决定，大家拉不开差距；能拉开差距的是编排和管束：工具契约怎么定、权限检查做多细、失败时降级成什么。Claude Code 为一条 bash 命令的意图判断写了完整的语法解析器，为一个路径检查覆盖了 ADS、8.3 短名、TOCTOU 这些对抗面。这笔投入换到的是&lt;strong&gt;信任半径&lt;/strong&gt;——用户敢让它在更多场景里放手干活。做智能体产品，&quot;聪明&quot;是租来的，&quot;管束&quot;才是自己的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-25&quot;&gt;结语：读代码，就是读决策&lt;/h2&gt;
&lt;p&gt;本文做的事概括起来就三步：从入口读出设计意图，从规模看出治理手段，从对照看清取舍代价。这套读法不挑对象，下次拿到任何一个陌生的代码库都可以照做一遍——先读入口，再数规模，最后找坐标。代码库不会主动告诉你它的设计决策，但它也撒不了谎；你需要的只是一套追问的顺序。&lt;/p&gt;
&lt;p&gt;而 Claude Code 这个案例真正值得记住的，不是某个具体的技术选型，而是那个反复出现的模式：&lt;strong&gt;入口处做安全分区，编译时做攻击面归零，契约里做默认不信任，接缝处做足预算。&lt;/strong&gt; 这四条合在一起，构成了一个 AI 工具敢把命令执行权交给模型的底气。当模型能力趋于同质化，&quot;敢让模型碰什么&quot;就成了产品分水岭——而这个问题的答案，早在代码还没运行起来的那一刻，就写在了入口文件里。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;原创技术博客 · 开源项目分享 · AI全栈创作社区  &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fidao.fun&quot; target=&quot;_blank&quot; title=&quot;https://idao.fun&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;idao.fun&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description><link>https://juejin.cn/post/7671128855915331611</link><guid isPermaLink="false">https://juejin.cn/post/7671128855915331611</guid><pubDate>Sat, 08 Aug 2026 07:17:08 GMT</pubDate><author>iDao技术魔方</author><category>前端</category><category>后端</category><category>Claude</category></item><item><title>用 AI 写代码半年，他忘了怎么手写 JSON</title><description>&lt;p&gt;Ken Liu 在 2012 年的短篇《The Perfect Match》里写过一个 AI 助手 Tilly，替用户决定早餐、音乐和约会对象。&lt;strong&gt;主角慢慢忘记自己喜欢什么，因为算法比他自己更懂他。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;十三年过去，小说里的 Tilly 变成了我们熟悉的 Claude、ChatGPT、Gemini、DeepSeek。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;有人已经开始把全部思考外包给 AI 了。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HN 上一位后端工程师说，自己做十几年开发，JSON 解析闭着眼都能写。后来项目赶进度，他把需求扔给 AI，几秒钟代码就出来了。用顺手之后，凡遇 JSON 相关问题，第一反应就是问 AI。慢慢地，他再看到 JSON，&lt;strong&gt;脑子里只剩一个模糊的感觉&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;直到有一天他想自己写一个 JSON 解析函数，手指悬在键盘上，愣住了。&lt;strong&gt;完全不知道怎么下手。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;那篇帖子下面很快堆了几百条回复。
有人面试时被问到基础算法题，当场脑子卡住；&lt;/p&gt;
&lt;p&gt;有人带新人时，发现自己能看懂 AI 写的代码，却解释不清为什么这样设计；&lt;/p&gt;
&lt;p&gt;有人项目做了两年，交接时翻出自己的代码，发现有三分之一自己都不认识。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI 提高了我们获得答案的速度。但它会不会也让我们减少了寻找答案的过程。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 data-id=&quot;heading-0&quot;&gt;人类一直在进行认知外包&lt;/h2&gt;
&lt;p&gt;把一部分能力交给工具，并不是 AI 时代才有的事。文字出现之前，知识主要依靠人的记忆保存。文字出现后，人类把大量信息放到了外部载体。&lt;/p&gt;
&lt;p&gt;后来，算盘替代部分计算，计算器替代复杂运算，搜索引擎替代大量事实记忆。这些变化并没有让人类失去能力。因为过去工具主要替代的是执行层面的重复劳动。计算器可以告诉你结果，但不会告诉你应该计算什么。搜索引擎可以提供资料，但不会替你判断哪个答案更可靠。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;工具减少的是执行成本。判断&quot;应该做什么&quot;这件事，始终需要人自己完成。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 data-id=&quot;heading-1&quot;&gt;AI 不同的地方，是它开始接近判断过程&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;AI 不只是帮我们保存信息或者执行任务。它开始参与判断过程本身。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个程序员以前解决一个问题，需要经历很多步骤。理解需求，判断这个需求是否合理。拆解问题，判断这个问题可以怎么拆分。尝试实现，遇到错误，分析错误原因，判断下一步怎么调整。&lt;/p&gt;
&lt;p&gt;这些步骤看起来很慢。&lt;strong&gt;但经验恰恰是在这个&quot;慢&quot;的过程中形成的。&lt;/strong&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;但 AI 可以直接跳过其中很多步骤。你描述问题，它给你一个能跑通的方案。结果可能完全正确，而你缺少了那个&quot;从混乱到清晰&quot;的判断过程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;你跳过的每一步里，都藏着一次判断。你以为在跳过&quot;麻烦&quot;，其实在跳过&quot;练习判断的机会&quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 data-id=&quot;heading-2&quot;&gt;被外包出去的是判断&lt;/h2&gt;
&lt;p&gt;很多人误以为，自己能看懂 AI 生成的代码，就代表自己掌握了。&lt;strong&gt;但理解答案和产生答案，是两种不同的能力。&lt;/strong&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;
&lt;h2 data-id=&quot;heading-3&quot;&gt;AI 重构大型项目没有想象中简单&lt;/h2&gt;
&lt;p&gt;这个问题不仅存在于个人开发场景。在工程领域，有人期待 AI 可以自动重写大型项目。分析代码，重新设计架构，自动优化实现。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;但最困难的部分，从来不是生成代码。而是理解代码背后的历史和取舍。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为什么这里用了一个看起来不优雅的方案。因为当时有不得不这样做的约束。为什么这个模块没有继续抽象。因为抽象的成本在当时超过了收益。为什么这个问题暂时没有解决。因为解决它需要付出的代价暂时还不值得。&lt;/p&gt;
&lt;p&gt;这些答案往往不在代码里，而在工程师长期积累的经验里。&lt;/p&gt;
&lt;p&gt;个人把写代码完全交给 AI，失去的是自己的判断过程。团队把工程决策完全交给 AI，失去的是整个系统的经验。&lt;strong&gt;判断可以被辅助，但不能被完全外包。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 data-id=&quot;heading-4&quot;&gt;区分效率提升和能力成长&lt;/h2&gt;
&lt;p&gt;那么应该少用 AI 吗。当然不是。&lt;/p&gt;
&lt;p&gt;未来大多数工作都会依赖 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;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;✅ 可外包&lt;/td&gt;&lt;td&gt;写测试用例、格式转换、整理资料、重复代码&lt;/td&gt;&lt;td&gt;效率&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;⚠️ 须自己练&lt;/td&gt;&lt;td&gt;首次设计数据库表、手写核心算法、第一次解决某类问题&lt;/td&gt;&lt;td&gt;能力&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;关键不在用不用 AI，而在你把它放在什么位置。如果你已经掌握一项能力，让 AI 处理重复部分，这是效率提升。但如果你第一次学习一个领域，就直接跳到 AI 给的最终答案，你得到的是结果，不是能力。&lt;/p&gt;
&lt;p&gt;同样是使用 AI，动机不同，结果就不同。&lt;strong&gt;让 AI 替你完成任务，你积累的是速度。让 AI 参与你的思考，你积累的是能力。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AI 可以帮我们更快拿到答案。但拿到答案之前的思考过程，才是判断力形成的地方。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;你最近有没有发现，某个你曾经很熟练的东西，现在已经想不起怎么下手了？&lt;/p&gt;</description><link>https://juejin.cn/post/7671231762140332038</link><guid isPermaLink="false">https://juejin.cn/post/7671231762140332038</guid><pubDate>Sat, 08 Aug 2026 06:45:32 GMT</pubDate><author>x_cmd</author><category>代码人生</category><category>JSON</category><category>Agent</category><category>AI编程</category></item><item><title></title><description></description><link>https://juejin.cn/pin/75488825233521869157671489093490769947</link><guid isPermaLink="false">https://juejin.cn/pin/75488825233521869157671489093490769947</guid><pubDate>Sat, 08 Aug 2026 05:49:52 GMT</pubDate><author>echoVic</author><category>Vibe 编程交流圈</category></item><item><title>Qwen 3.8 max干了20分钟没干完，免费模型3分17秒搞定，问题出在哪？</title><description>&lt;p&gt;&lt;strong&gt;大家好，我是老刘&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;前两天老刘花了一杯咖啡的钱，买了千问token plan的最低档，准备测试一下Qwen 3.8 max在我的Flutter项目里能不能当主力。&lt;/p&gt;
&lt;p&gt;任务很简单：我已经开发完了三个独立的数据处理和业务逻辑模块，现在只需要把它们接入主流程，让主流程能调用它们。&lt;/p&gt;
&lt;p&gt;就这么个活儿，Qwen 3.8 max干了20分钟，直接把我的5小时额度烧光了，然后工作还没完成。停在了一半的地方。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/90f0c609d0604e1d9e317aaad9f392f5~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg56iL5bqP5ZGY6ICB5YiY:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786767861&amp;amp;x-signature=PUy8GEVvoUL65H6Wr6Omxxm5EUI%3D&quot; alt=&quot;&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;我换成opencode的免费模型，同样的任务，3分17秒，顺利完成。&lt;/p&gt;
&lt;p&gt;不可思议是不是？&lt;/p&gt;
&lt;p&gt;这完全不是一个对标顶级模型应该有的表现。&lt;/p&gt;
&lt;p&gt;今天老刘就来聊聊，这20分钟里到底发生了什么，以及我们选主力模型时最容易踩的那个坑。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 data-id=&quot;heading-0&quot;&gt;先说句公道话，Qwen 3.8 max的跑分是真能打&lt;/h3&gt;
&lt;p&gt;这里要先说明白，老刘说的测评数据不是那种公开题目可以刷榜的测评。&lt;/p&gt;
&lt;p&gt;那种测评我基本已经不看了，因为吃过几次亏：评分很高，但实际工作能力很差。&lt;/p&gt;
&lt;p&gt;我现在主要看题目不公开的，比如LiveBench。&lt;/p&gt;
&lt;p&gt;Qwen 3.8 max在这类测评中得分很高，说明真实能力还是可圈可点的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/40d5b8b6c92f4d7d89f284d8f70e4845~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg56iL5bqP5ZGY6ICB5YiY:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786767861&amp;amp;x-signature=QG7WuBo%2BBmJ123atJQHwQVLQjzA%3D&quot; alt=&quot;&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;可以看到，Qwen 3.8 max的综合能力仅次于claude fable 5和gpt-5.6这样的顶级模型。&lt;/p&gt;
&lt;p&gt;单看编程能力，也非常好：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/2d4281ebff3f49d682e429905a9c9613~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg56iL5bqP5ZGY6ICB5YiY:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786767861&amp;amp;x-signature=UQvpaxMc9AVXtNLgWpGTA2klCyk%3D&quot; alt=&quot;&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;闭源题目的测评具有比较高的参考意义，在一定程度上能体现模型在指定科目上的泛化能力。&lt;/p&gt;
&lt;p&gt;但要注意这个是参考价值，因为测评和真实项目之间还是存在着比较大的差异的。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 data-id=&quot;heading-1&quot;&gt;跑分为啥救不了你的项目？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;场景对不上&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;老刘主要写Flutter代码，但目前主流编程能力测评里，基本没有用Flutter的。&lt;/p&gt;
&lt;p&gt;你拿一个考Python和Web的分数，去预测它写Flutter的水平，这就像拿驾照科目一的成绩去判断一个人会不会开山路。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;用法对不上&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;老刘的日常开发流程是这样的：先用顶级模型把一个需求拆成多个功能点，再用中档的主力模型逐个开发每个功能点的代码。&lt;/p&gt;
&lt;p&gt;这个用法其实介于LiveBench的coding和agentic coding之间。&lt;/p&gt;
&lt;p&gt;也就是说，我这个功能点的开发，用测评里的哪个维度去选模型都不合适。即使用平均分，偏差也很大。&lt;/p&gt;
&lt;p&gt;所以测评的分数主要还是一个参考，最终还是要放到项目里实际跑一跑。&lt;/p&gt;
&lt;p&gt;老刘这次就是真刀真枪地跑了。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 data-id=&quot;heading-2&quot;&gt;Qwen 3.8 max还是是干活太认真了&lt;/h3&gt;
&lt;p&gt;如果用一个字总结Qwen 3.8 max在我项目里的表现，就是啰嗦。&lt;/p&gt;
&lt;p&gt;看它的思考过程就能发现问题：思考链路特别长，如果每一轮都把上一轮的思考加入输入，token很快就消耗光了。&lt;/p&gt;
&lt;p&gt;这一点从LiveBench的性价比排名中也能得到印证：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/00e57a385987409d93c00b4dd89c1c13~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg56iL5bqP5ZGY6ICB5YiY:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786767861&amp;amp;x-signature=TL9AoYaC%2FWdETEw3LFCwFGPMijE%3D&quot; alt=&quot;&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;p&gt;单看简单编程的性价比，Qwen 3.8 max的排名并不靠前。&lt;/p&gt;
&lt;p&gt;所以老刘的结论是：Qwen 3.8 max的能力确实不错，但代价是需要消耗大量的token才能完成同样的任务。&lt;/p&gt;
&lt;p&gt;这就好比招了一个能力很强的员工，但他干一个小时的活儿要烧别人一天的工资。&lt;/p&gt;
&lt;p&gt;从测评看，它在逻辑思考、数据处理等方面的性价比明显比编程方面高。让它当编程主力，可能属于专业不对口。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有意思的是，当前这篇文章的错字修改、排版优化、文章头尾固定内容的添加、发布到github等工作都是老刘用token plan中仅剩的额度，通过Qwen 3.8 max完成的。总体消耗远小于开发任务。&lt;/p&gt;
&lt;p&gt;这也从侧面证明了老刘的观点，Qwen 3.8 max似乎更适合非编程类任务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 data-id=&quot;heading-3&quot;&gt;一杯咖啡的钱，买了个教训&lt;/h3&gt;
&lt;p&gt;说实话，这次测试老刘自己也有点尴尬。&lt;/p&gt;
&lt;p&gt;我买这个最低档的token plan，本来是想横向测试一下各家订阅哪个更好用，后续也会给大家汇报其他厂商订阅的使用效果。&lt;/p&gt;
&lt;p&gt;结果千问这边，第一轮长对话还没跑完，额度就阵亡了。&lt;/p&gt;
&lt;p&gt;这里还是要吐槽一句：虽然最低档只要一杯咖啡的钱，但如果5小时的限额连一轮长对话都无法完成，那它在实际工作中就没有任何意义。&lt;/p&gt;
&lt;p&gt;特别是在agnes、商汤、Openrouter、opencode等很多厂商都提供了慷慨的免费额度的时候，这种低档位订阅完全不具备在开发场景中的实用价值。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 data-id=&quot;heading-4&quot;&gt;最后说两句&lt;/h3&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;blockquote&gt;
&lt;p&gt;🤝 如果看到这里的同学对客户端或者Flutter开发感兴趣，欢迎联系老刘，我们互相学习。&lt;/p&gt;
&lt;p&gt;🎁 私信免费领老刘整理的《Flutter开发手册》，覆盖90%应用开发场景。可以作为Flutter学习的知识地图。&lt;/p&gt;
&lt;p&gt;&amp;nbsp;💬 : laoliu_dev&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;📂 老刘也把自己历史文章整理在GitHub仓库里，方便大家查阅。&lt;/p&gt;
&lt;p&gt;🔗 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Flzt-code%2Fblog&quot; target=&quot;_blank&quot; title=&quot;https://github.com/lzt-code/blog&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;github.com/lzt-code/bl…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description><link>https://juejin.cn/post/7671185982583341108</link><guid isPermaLink="false">https://juejin.cn/post/7671185982583341108</guid><pubDate>Sat, 08 Aug 2026 04:24:22 GMT</pubDate><author>程序员老刘</author><category>开发工具</category><category>AI编程</category><category>Flutter</category></item><item><title>复制成功，不等于文章能发布：markdown2x 如何拆开富文本与图片资产</title><description>&lt;p&gt;把一篇 Markdown 复制进 X Articles，看起来只有两个步骤：解析 Markdown，写入剪贴板。&lt;/p&gt;
&lt;p&gt;真正动手后，很快就会撞到几个不在同一层的问题。标题和列表要保留格式；代码块粘贴后不能变成一团等宽文本；Mermaid 和表格需要转成图片；浏览器还可能拒绝富文本剪贴板权限。即使按钮提示“复制成功”，文章里仍可能留着没有上传的图片和平台不支持的语法。&lt;/p&gt;
&lt;p&gt;我最近重新读了 markdown2x 的复制链路。它没有把所有内容硬塞进一段 HTML，而是先把一篇文章拆成文档结构，再同时生成富文本、纯文本和待处理资产。这种拆法比“Markdown 转 HTML”更接近真实的发布流程。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-0&quot;&gt;先得到文档结构，再决定怎么交付&lt;/h2&gt;
&lt;p&gt;入口在 &lt;code&gt;lib/markdown.ts&lt;/code&gt; 的 &lt;code&gt;parseMarkdown()&lt;/code&gt;。它把源码解析成一组 &lt;code&gt;ArticleBlock&lt;/code&gt;：标题、段落、引用、列表、代码、Mermaid、表格和 Tweet 各自拥有明确类型；行内内容再拆成文本、粗体、斜体、链接、行内代码和图片。&lt;/p&gt;
&lt;p&gt;这里没有直接输出 HTML。预览、纯文本回退、剪贴板富文本和资产面板都消费同一份结构。&lt;/p&gt;
&lt;p&gt;这个中间层解决了一个很实际的问题：同一个 Markdown 块，在不同出口里应该有不同形态。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;二级标题在富文本里是 &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;，在纯文本里只保留文字。&lt;/li&gt;
&lt;li&gt;链接在 HTML 里保留 &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt;，在纯文本里变成“标题 + URL”。&lt;/li&gt;
&lt;li&gt;代码块可以变成引用，也可以变成一张待粘贴的 PNG。&lt;/li&gt;
&lt;li&gt;Mermaid 源码不能直接进入文章正文，只留下图片插入位置。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果解析阶段就急着拼 HTML，后面每增加一种出口都要重新猜一次原始语义。&lt;code&gt;ArticleBlock&lt;/code&gt; 把这次猜测固定在一个地方。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-1&quot;&gt;一个复制按钮，实际写入两份正文&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;toXArticleClipboard()&lt;/code&gt; 返回的不是一个字符串，而是一份 &lt;code&gt;XArticleClipboard&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ts&quot; lang=&quot;ts&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;XArticleClipboard&lt;/span&gt; = {
  &lt;span class=&quot;hljs-attr&quot;&gt;title&lt;/span&gt;: &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;titleHtml&lt;/span&gt;: &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;bodyText&lt;/span&gt;: &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;bodyHtml&lt;/span&gt;: &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;fullText&lt;/span&gt;: &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;fullHtml&lt;/span&gt;: &lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;assets&lt;/span&gt;: &lt;span class=&quot;hljs-title class_&quot;&gt;XArticleAsset&lt;/span&gt;[];
  &lt;span class=&quot;hljs-attr&quot;&gt;assetOffsets&lt;/span&gt;: &lt;span class=&quot;hljs-built_in&quot;&gt;number&lt;/span&gt;[];
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正文复制时，&lt;code&gt;components/editor-page.tsx&lt;/code&gt; 会同时写入两种 MIME：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ts&quot; lang=&quot;ts&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;ClipboardItem&lt;/span&gt;({
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/html&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Blob&lt;/span&gt;([html], { &lt;span class=&quot;hljs-attr&quot;&gt;type&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/html&quot;&lt;/span&gt; }),
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Blob&lt;/span&gt;([text], { &lt;span class=&quot;hljs-attr&quot;&gt;type&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt; }),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;支持富文本的目标编辑器会读取 &lt;code&gt;text/html&lt;/code&gt;，普通输入框仍可退回 &lt;code&gt;text/plain&lt;/code&gt;。用户只点了一次按钮，剪贴板里却带着两个兼容层。&lt;/p&gt;
&lt;p&gt;浏览器拒绝 &lt;code&gt;ClipboardItem&lt;/code&gt; 时，代码还会退回到一个屏幕外的 &lt;code&gt;contenteditable&lt;/code&gt; 容器，通过 Selection 和 &lt;code&gt;document.execCommand(&quot;copy&quot;)&lt;/code&gt; 复制。这个路径也失败，界面才展示手动复制区。&lt;/p&gt;
&lt;p&gt;所以“复制正文”不是一次 API 调用。它是一条有降级顺序的交付链：现代富文本剪贴板、Selection 回退、人工复制。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-2&quot;&gt;代码、表格和 Mermaid 被当成另一类工件&lt;/h2&gt;
&lt;p&gt;X Articles 的正文编辑器不能可靠接住所有 Markdown 结构。markdown2x 没有假装一次粘贴能解决全部内容。&lt;/p&gt;
&lt;p&gt;当代码模式设为 &lt;code&gt;image&lt;/code&gt; 时，代码块与表格、Mermaid 会进入 &lt;code&gt;assets&lt;/code&gt;。正文对应位置只留下明确提示，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;📷 [Code image 1 — paste from Assets panel]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个资产还保存自己的原始数据：代码与语言、表头与行、Mermaid 源码。&lt;code&gt;assetOffsets&lt;/code&gt; 则记录它在 Markdown 源文中的字符位置，后续可以从检查结果或资产面板跳回编辑器。&lt;/p&gt;
&lt;p&gt;这一步很像构建系统处理源码和产物：正文是可直接粘贴的主工件，图片是需要单独渲染和上传的伴随工件，offset 是两者回到源文件的定位信息。&lt;/p&gt;
&lt;p&gt;项目没有把图片伪装成已经交付。测试会明确断言 Mermaid 源码不进入 &lt;code&gt;bodyHtml&lt;/code&gt;，而 &lt;code&gt;assets&lt;/code&gt; 中保留完整图表内容；表格同样转成 PNG 任务，Tweet 则保留链接和嵌入提示。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-3&quot;&gt;Preflight 能发现问题，但目前不会拦住复制&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;runPreflight()&lt;/code&gt; 会扫描两类风险。&lt;/p&gt;
&lt;p&gt;一类来自已解析结构：代码将变成引用还是 PNG、表格有多少列、远程图片不会自动上传。另一类直接扫描原始 Markdown，包括脚注、LaTeX、iframe、分隔线和删除线等当前不支持的语法。&lt;/p&gt;
&lt;p&gt;每条结果都带 &lt;code&gt;sourceOffset&lt;/code&gt;，用户可以跳回问题位置。报告状态分为 &lt;code&gt;ok&lt;/code&gt;、&lt;code&gt;warning&lt;/code&gt; 和 &lt;code&gt;blocked&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但这里有一个需要说清的边界：&lt;code&gt;copyBody()&lt;/code&gt; 在没有 preflight 报告时只显示五秒提示，随后仍会继续复制。也就是说，当前 preflight 是发布前检查工具，还不是一个强制 gate。&lt;code&gt;blocked&lt;/code&gt; 描述的是检查结果，不代表复制按钮一定被阻止。&lt;/p&gt;
&lt;p&gt;这并非文字游戏。写文档工具时，提示、阻断和自动修复是三种不同承诺。把提示写成“已经保证可以发布”，会让用户在目标平台上才发现缺图或语法丢失。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-4&quot;&gt;转换工具真正交付的是失败边界&lt;/h2&gt;
&lt;p&gt;这条源码链让我重新理解了文档转换工具。格式转换只是其中一段，完整交付还包括：目标编辑器能吃下什么，浏览器剪贴板允许什么，哪些内容必须变成独立资产，以及失败后用户从哪里继续。&lt;/p&gt;
&lt;p&gt;markdown2x 目前已经把这些事实拆开：同一份 AST 生成 HTML 和纯文本，图片类内容进入资产队列，preflight 标出无法自动处理的语法，复制权限失败后继续降级。&lt;/p&gt;
&lt;p&gt;它也保留了真实限制。图片仍需要用户逐个粘贴，远程图片不会自动上传，preflight 还没有成为强制发布门。现阶段把这些边界展示出来，比用一个“完美转换”的按钮文案更有用。&lt;/p&gt;
&lt;p&gt;项目源码在 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FechoVic%2Fx-article-md&quot; target=&quot;_blank&quot; title=&quot;https://github.com/echoVic/x-article-md&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;echoVic/x-article-md&lt;/a&gt;，线上工具使用 &lt;code&gt;markdown2x.com&lt;/code&gt;。本文对应的转换契约集中在 &lt;code&gt;lib/markdown.ts&lt;/code&gt;、&lt;code&gt;lib/preflight.ts&lt;/code&gt; 和 &lt;code&gt;components/editor-page.tsx&lt;/code&gt;。&lt;/p&gt;</description><link>https://juejin.cn/post/7671231304078164019</link><guid isPermaLink="false">https://juejin.cn/post/7671231304078164019</guid><pubDate>Sat, 08 Aug 2026 04:13:25 GMT</pubDate><author>echoVic</author><category>开发工具</category><category>Markdown</category><category>AI编程</category></item><item><title>Han：一个让 AI Agent 也能做出高级中国风页面的 CSS 设计系统</title><description>&lt;p&gt;说实话，挺难的。&lt;/p&gt;
&lt;p&gt;你要朱红，但不能太红；你要水墨，但不能太黑；你要留白，但不能空得没东西。一不小心，要么变成喜庆大红大绿，要么变成&quot;假古董&quot;堆龙凤祥云。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;汉 · Han&lt;/strong&gt; 就是为了解决这个事儿——一个面向 AI Agent 的中国文化设计 Skill，把宣纸、墨色、朱红、书法、卷轴这些东方美学元素，变成可以直接用的 CSS 令牌、主题和组件。&lt;/p&gt;
&lt;p&gt;不用从零配色，不用手写纹样，不用纠结&quot;这个朝代该用什么颜色&quot;。&lt;strong&gt;引入一个 CSS，选个主题，页面就有了气质。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-0&quot;&gt;一句话介绍：它是个啥？&lt;/h2&gt;
&lt;p&gt;Han 是一个设计 Skill + 视觉资产包。你可以把它理解为：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一套&lt;strong&gt;开箱即用的中国风设计系统&lt;/strong&gt;，CSS 层面帮你搞定色彩、字体、间距、组件和动效，同时附带文化决策规则，让 AI Agent 能自主判断&quot;什么场景该用什么风格&quot;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;目前有 &lt;strong&gt;22 个主题&lt;/strong&gt;，覆盖从秦汉到清的朝代启发主题、水墨/青花瓷/敦煌等文化主题，还有 6 个面向现代网页的当代配色主题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不用 build，不用装依赖&lt;/strong&gt;，引入 CSS 就能用。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-1&quot;&gt;能做什么？直接看例子&lt;/h2&gt;
&lt;p&gt;光说没用，看看用 Han 做出来的真实页面长什么样。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-2&quot;&gt;1. 茶叶品牌 — 松麦主题&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fhan.raingpt.top%2Fpine-wheat-tea&quot; target=&quot;_blank&quot; title=&quot;https://han.raingpt.top/pine-wheat-tea&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;han.raingpt.top/pine-wheat-…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;松麦绿配色，自然清新。导航、Hero 区、产品展示、品牌故事、CTA，一个茶叶品牌的落地页该有的全有了。配色不抢戏，气质对味。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d609cf194a814c41a9ec9e244e388b0a~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Zuo5aSc5a-75pm05aSp:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786766630&amp;amp;x-signature=q51%2BhZnIZGFULJCTEPaWAkfGGXg%3D&quot; alt=&quot;image.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-3&quot;&gt;2. 美妆护肤 — 藤紫主题&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fhan.raingpt.top%2Fplum-blush-beauty&quot; target=&quot;_blank&quot; title=&quot;https://han.raingpt.top/plum-blush-beauty&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;han.raingpt.top/plum-blush-…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;藤紫色调，柔和浪漫。适合美妆、护肤、轻奢品牌。谁说中国风只能配茶和博物馆？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/9ff65d97b3264e6685ed8ef23c508912~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Zuo5aSc5a-75pm05aSp:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786766630&amp;amp;x-signature=Z6uvmJkZq02vJWjkiZZqKD1hG7U%3D&quot; alt=&quot;image.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-4&quot;&gt;3. AI 科技产品 — 海兰主题&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fhan.raingpt.top%2Focean-orchid-ai&quot; target=&quot;_blank&quot; title=&quot;https://han.raingpt.top/ocean-orchid-ai&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;han.raingpt.top/ocean-orchi…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;海兰配色，冷静清透。科技产品也能用中国风，而且一点都不违和。谁说做 AI 就得蓝紫渐变？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/452f0a3a1baa4fcaab212ccfcf9374b3~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Zuo5aSc5a-75pm05aSp:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786766630&amp;amp;x-signature=aRjEGP5xDWHkTvLo2WdrzlGt6S8%3D&quot; alt=&quot;image.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-5&quot;&gt;4. 精品咖啡 — 焦糖主题&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fhan.raingpt.top%2Fcaramel-cream-coffee&quot; target=&quot;_blank&quot; title=&quot;https://han.raingpt.top/caramel-cream-coffee&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;han.raingpt.top/caramel-cre…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;焦糖暖色，复古高级。咖啡品牌 + 中国风，这个组合可能很多人没想到，但效果真的很搭。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/eedd1624ac9e4ab8a0b33d1847274724~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Zuo5aSc5a-75pm05aSp:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786766630&amp;amp;x-signature=z6FnDewBxW6kGYiim9cdlTOD%2FJI%3D&quot; alt=&quot;image.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-6&quot;&gt;5. 瑜伽健康 — 薄荷主题&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fhan.raingpt.top%2Fmint-lavender-yoga&quot; target=&quot;_blank&quot; title=&quot;https://han.raingpt.top/mint-lavender-yoga&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;han.raingpt.top/mint-lavend…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;薄荷薰衣草配色，清透自然。瑜伽、健康、生活方式类产品用这个，看着就让人想深呼吸。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/0885d60ad8e846409d63ef1956f2df11~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Zuo5aSc5a-75pm05aSp:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786766630&amp;amp;x-signature=%2Fem2yeBqDHizaXwpRQJGkAIz4h4%3D&quot; alt=&quot;image.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-7&quot;&gt;6. 甜品节活动 — 莓果主题&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fhan.raingpt.top%2Fberry-butter-dessert&quot; target=&quot;_blank&quot; title=&quot;https://han.raingpt.top/berry-butter-dessert&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;han.raingpt.top/berry-butte…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;莓果奶油配色，明快有活力。活动页、营销页用这个色调，年轻又不轻浮。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/9a084cbfad2e46329e45265d6d71979c~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Zuo5aSc5a-75pm05aSp:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786766630&amp;amp;x-signature=Njnpi5faOtYrz4bDCkPVVj0o0uE%3D&quot; alt=&quot;image.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-8&quot;&gt;7. Mac 产品概念页 — 宋韵主题&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fhan.raingpt.top%2Flanding-page&quot; target=&quot;_blank&quot; title=&quot;https://han.raingpt.top/landing-page&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;han.raingpt.top/landing-pag…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个可能最让人意外——用宋韵主题做一个 Apple Mac 风格的产品页。清雅克制的设计语言，配上产品大图和精致排版，&lt;strong&gt;东方美学和现代科技产品的结合，居然可以这么自然&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/d5fd6a83f5f7419bb499bd7274c209b9~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Zuo5aSc5a-75pm05aSp:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786766630&amp;amp;x-signature=OZb6UKVvS2C1vM%2Fe0Kta0zGqYkU%3D&quot; alt=&quot;image.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-9&quot;&gt;8. 软件产品官网介绍&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fluma.raingpt.top%2F&quot; target=&quot;_blank&quot; title=&quot;https://luma.raingpt.top/&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;luma.raingpt.top/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本页由 Han design skill（宋 · song 主题）构建 · 响应式 · 可访问&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/2eea4badf126415dbe38d0055bd54b3e~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg6Zuo5aSc5a-75pm05aSp:q75.awebp?rk3s=f64ab15b&amp;amp;x-expires=1786766630&amp;amp;x-signature=ob0CQzLpkK%2BCscNY1roIS6%2FHn7I%3D&quot; alt=&quot;image.png&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-10&quot;&gt;9. 当代配色主题总览&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;📍 &lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fhan.raingpt.top%2Fcontemporary-themes&quot; target=&quot;_blank&quot; title=&quot;https://han.raingpt.top/contemporary-themes&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;han.raingpt.top/contemporar…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;6 个当代配色主题一站式展示，带主题切换、色彩体系展示和组件 demo。想选哪个主题，打开这个页面直接对比。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-11&quot;&gt;核心能力：不只是好看&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-12&quot;&gt;22 个主题，总有一个对味&lt;/h3&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;类型&lt;/th&gt;&lt;th&gt;主题&lt;/th&gt;&lt;th&gt;适合场景&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;朝代启发&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;秦汉、魏晋、唐、宋、元、明、清&lt;/td&gt;&lt;td&gt;博物馆、文化展览、历史教育&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;文化主题&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;水墨、青绿山水、青花瓷、敦煌、宫廷金、武侠、茶道、朱砂、青瓷&lt;/td&gt;&lt;td&gt;品牌官网、文创产品、传统文化&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;当代配色&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;松麦、藤紫、海兰、焦糖、薄荷、莓果&lt;/td&gt;&lt;td&gt;现代品牌、SaaS 产品、生活方式&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;切主题就一行代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot; lang=&quot;html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;lang&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;zh-CN&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-theme&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;song&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-id=&quot;heading-13&quot;&gt;组件都有，而且不土&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;印章按钮&lt;/strong&gt; &lt;code&gt;.han-btn-seal&lt;/code&gt; — 关键 CTA 用，带倾斜和盖章动效&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;水墨导航&lt;/strong&gt; &lt;code&gt;.han-navbar&lt;/code&gt; — sticky + 毛玻璃，品牌名一个字朱红&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;卷轴卡片&lt;/strong&gt; &lt;code&gt;.han-card-scroll&lt;/code&gt; — 产品展示、文章列表，四角铆钉装饰&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;窗花边框&lt;/strong&gt; &lt;code&gt;.han-frame-window&lt;/code&gt; — 名言警句、诗词展示&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;水墨分割线&lt;/strong&gt; &lt;code&gt;.han-divider-ink&lt;/code&gt; — 章节过渡，带小印章&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;印章&lt;/strong&gt; &lt;code&gt;.han-stamp&lt;/code&gt; — 署名、落款、认证&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-id=&quot;heading-14&quot;&gt;AI Agent 能自己干活&lt;/h3&gt;
&lt;p&gt;这是 Han 最有意思的地方——它不只是 CSS，还包含一套&lt;strong&gt;文化决策规则&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;你跟 AI 说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;用 han-design，帮我做一个克制的宋韵产品落地页。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;或者更随意：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;用 han-design 把当前页面弄好看点，你自己判断主题和风格。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;AI 会自己读设计规范、判断页面类型、选主题、定视觉强度、补全内容，然后生成页面。&lt;strong&gt;你不需要知道主题 ID 叫什么，也不需要背组件类名。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-15&quot;&gt;该有的工程素质都有&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;响应式，移动端能看&lt;/li&gt;
&lt;li&gt;键盘焦点、ARIA 标签、reduced-motion 支持&lt;/li&gt;
&lt;li&gt;&lt;code&gt;han-scoped.css&lt;/code&gt; 局部引入，不污染宿主项目全局样式&lt;/li&gt;
&lt;li&gt;无 build 步骤，静态 HTML 直接跑&lt;/li&gt;
&lt;li&gt;深色模式支持&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-16&quot;&gt;怎么用？&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-17&quot;&gt;方式一：作为 Agent Skill 安装&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; https://github.com/you-want/han-design.git han

&lt;span class=&quot;hljs-built_in&quot;&gt;mkdir&lt;/span&gt; -p &lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/.agents/skills&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-built_in&quot;&gt;cp&lt;/span&gt; -R skills/han-design &lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/.agents/skills/han-design&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;装完直接跟 AI 说&quot;用 han-design 做个页面&quot;就行。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-18&quot;&gt;方式二：直接用 CSS&lt;/h3&gt;
&lt;p&gt;复制 &lt;code&gt;skills/han-design/assets/&lt;/code&gt; 到你的项目：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot; lang=&quot;html&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;&amp;lt;!-- 可选：远程字体 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;rel&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;/han/fonts.css&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;&amp;lt;!-- 完整设计系统 --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;rel&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;/han/han.css&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;已有项目不想被全局 reset 影响？用 scoped 版：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot; lang=&quot;html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;rel&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;href&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;/han/han-scoped.css&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;section&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-han-scope&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;data-theme&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;song&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;type&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;button&quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;han-btn-seal&quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;确认&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;button&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;section&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Token、深色模式和主题变量只在 &lt;code&gt;data-han-scope&lt;/code&gt; 内生效，&lt;strong&gt;不动你项目里其他任何样式&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-19&quot;&gt;适合谁用？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;前端开发者&lt;/strong&gt; — 想快速做个有调性的中国风页面，不想从零配色&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设计师&lt;/strong&gt; — 需要一套靠谱的中国风设计令牌做参考&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 应用开发者&lt;/strong&gt; — 给 Agent 装上&quot;中国风审美&quot;的能力&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;独立开发者 / 创业者&lt;/strong&gt; — 做品牌官网、产品落地页，想要点不一样的气质&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;任何觉得&quot;蓝紫渐变 + Inter 字体&quot;看腻了的人&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-20&quot;&gt;最后说两句&lt;/h2&gt;
&lt;p&gt;做中国风设计最大的坑，不是技术，是&lt;strong&gt;分寸感&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;朱红用多了像过年，金色堆多了像寺庙，龙凤纹样乱用显得廉价。Han 的设计规范里专门写了&lt;strong&gt;禁忌清单&lt;/strong&gt;——金色不超过 3%，朱红不超过 5%，不用 emoji 当图标，不乱用龙凤纹样。这些规则不是拍脑袋定的，是从大量实践里总结出来的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;让东方美学在网页上优雅绽放，而不是用力过猛。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就是 Han 想做的事。&lt;strong&gt;为啥叫做 Han ？？？ 因为我们身份证是汉族、说的是汉语、写的是汉字......那么当然我们的这个中国风也要叫 han-design&lt;/strong&gt; 。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;项目地址&lt;/strong&gt;：&lt;a href=&quot;https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fyou-want%2Fhan-design&quot; target=&quot;_blank&quot; title=&quot;https://github.com/you-want/han-design&quot; ref=&quot;nofollow noopener noreferrer&quot;&gt;github.com/you-want/ha…&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;License&lt;/strong&gt;：MIT，随便用。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;以上就是全部内容。如果觉得有意思，去 GitHub 点个 Star，或者直接装来试试。有问题欢迎提 Issue，也欢迎来交流你用 Han 做出了什么好东西。&lt;/p&gt;
&lt;/blockquote&gt;</description><link>https://juejin.cn/post/7671186598323339305</link><guid isPermaLink="false">https://juejin.cn/post/7671186598323339305</guid><pubDate>Sat, 08 Aug 2026 04:03:50 GMT</pubDate><author>雨夜寻晴天</author><category>人工智能</category><category>前端</category><category>设计</category><category>AI编程</category></item><item><title>前端看后端 16：什么是端口？</title><description>&lt;blockquote&gt;
&lt;p&gt;专栏第十六篇，网络与协议篇第九章。上一篇我们聊了 DNS，搞懂了浏览器是怎么把 &lt;code&gt;google.com&lt;/code&gt; 翻译成 &lt;code&gt;142.250.72.206&lt;/code&gt; 这个 IP 的。但问题来了——数据包千辛万苦传到服务器，服务器上同时跑着 Nginx、Node.js、MySQL、Redis、SSH……这包到底该交给谁？总不能让所有程序抢着处理吧？今天的主角 &lt;strong&gt;Port（端口）&lt;/strong&gt;，就是操作系统用来区分&quot;这台机器上不同程序&quot;的编号。它是网络通信里最容易被忽略、却又天天打交道的概念——&lt;code&gt;3000&lt;/code&gt;、&lt;code&gt;5173&lt;/code&gt;、&lt;code&gt;8080&lt;/code&gt;、&lt;code&gt;3306&lt;/code&gt;、&lt;code&gt;6379&lt;/code&gt; 这些数字背后，藏着一整套规则。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-0&quot;&gt;一、端口是什么？——IP 是酒店地址，端口是房间号&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;端口（Port）是一个 16 位整数，范围 0 ~ 65535&lt;/strong&gt;，用于在一台主机上&lt;strong&gt;区分不同的应用程序或服务&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-1&quot;&gt;核心比喻&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;IP 地址 = 酒店地址（如 142.250.72.206）
端口   = 房间号（如 443）

访问 https://google.com
= 找到&quot;142.250.72.206 这家酒店&quot;的&quot;443 号房间&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IP 解决&quot;哪台机器&quot;&lt;/strong&gt;（网络层）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;端口解决&quot;机器上的哪个程序&quot;&lt;/strong&gt;（传输层）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 一台服务器可以同时跑很多服务，全靠端口区分。没有端口，数据到了机器也不知道交给谁——就像快递只写了酒店地址没写房间号，前台只能干瞪眼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-id=&quot;heading-2&quot;&gt;为什么需要端口？&lt;/h3&gt;
&lt;p&gt;假设没有端口：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你的服务器同时跑着 Web 服务、数据库、SSH、FTP……&lt;/li&gt;
&lt;li&gt;一个数据包从网络到达网卡&lt;/li&gt;
&lt;li&gt;操作系统内核懵了：&quot;这包是给谁的？&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;端口就是操作系统做&quot;多路复用&quot;的依据&lt;/strong&gt;：内核维护一张&quot;端口 → 进程&quot;的映射表，数据包到达时，根据目标端口，交给对应的进程处理。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-3&quot;&gt;二、端口的分类（0~65535 的三段划分）&lt;/h2&gt;
&lt;p&gt;IANA（互联网号码分配机构）把端口分成三段，每段用途不同。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-4&quot;&gt;1️⃣ 知名端口（Well-Known Ports）：0 ~ 1023&lt;/h3&gt;
&lt;p&gt;分配给最核心的互联网服务，&lt;strong&gt;在 Linux/Unix 上需要 root 权限才能绑定&lt;/strong&gt;。&lt;/p&gt;








































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;端口&lt;/th&gt;&lt;th&gt;服务&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;80&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTTP&lt;/td&gt;&lt;td&gt;网页（明文）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;443&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTTPS&lt;/td&gt;&lt;td&gt;网页（加密）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;22&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;SSH&lt;/td&gt;&lt;td&gt;远程登录&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;21&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;FTP&lt;/td&gt;&lt;td&gt;文件传输（控制通道）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;25&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;SMTP&lt;/td&gt;&lt;td&gt;发邮件&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;53&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;DNS&lt;/td&gt;&lt;td&gt;域名解析&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ 注意：MySQL（3306）、Redis（6379）、PostgreSQL（5432）这些虽然&quot;知名&quot;，但端口号 &lt;strong&gt;大于 1023&lt;/strong&gt;，严格来说属于&quot;注册端口&quot;，不是知名端口。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-id=&quot;heading-5&quot;&gt;2️⃣ 注册端口（Registered Ports）：1024 ~ 49151&lt;/h3&gt;
&lt;p&gt;分配给特定的应用程序或服务，由 IANA 登记管理。开发者自己写的服务，通常也在这段里选端口。&lt;/p&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;端口&lt;/th&gt;&lt;th&gt;服务&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;3306&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;MySQL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;5432&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;PostgreSQL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;6379&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Redis&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;27017&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;MongoDB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;5672&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;RabbitMQ&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;8080&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTTP 替代端口（Tomcat 默认）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;8443&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTTPS 替代端口&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;9090&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Prometheus&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;9200&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Elasticsearch&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 data-id=&quot;heading-6&quot;&gt;3️⃣ 动态/私有端口（Dynamic/Private Ports）：49152 ~ 65535&lt;/h3&gt;
&lt;p&gt;客户端&lt;strong&gt;临时使用&lt;/strong&gt;的端口，也叫&quot;临时端口&quot;（ephemeral port）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当你访问 &lt;code&gt;google.com:443&lt;/code&gt; 时，操作系统会从 49152~65535 中随机挑一个端口作为&lt;strong&gt;源端口&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;服务器响应时，数据就发回这个端口&lt;/li&gt;
&lt;li&gt;连接关闭后，端口释放，可被下次复用&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;📌 你在 &lt;code&gt;netstat&lt;/code&gt; 里看到的那些 &lt;code&gt;localhost:51023&lt;/code&gt;、&lt;code&gt;localhost:52341&lt;/code&gt;，基本都是临时端口。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-7&quot;&gt;三、TCP 端口 vs UDP 端口&lt;/h2&gt;
&lt;p&gt;端口同时存在于 TCP 和 UDP 两个协议上，&lt;strong&gt;它们是独立的&lt;/strong&gt;！&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;TCP:80   ≠   UDP:80
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TCP 端口&lt;/strong&gt;：面向连接的服务（HTTP、HTTPS、SSH、MySQL）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UDP 端口&lt;/strong&gt;：无连接的服务（DNS 查询、DHCP、视频流、游戏）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以一台机器上，&lt;strong&gt;TCP 80 和 UDP 80 可以同时被不同进程占用&lt;/strong&gt;，互不影响。操作系统内部实际上维护了两张表：一张 TCP 的，一张 UDP 的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-8&quot;&gt;四、常见端口速查表（开发者必备）&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-9&quot;&gt;🌐 Web 相关&lt;/h3&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;端口&lt;/th&gt;&lt;th&gt;用途&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;80&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTTP（浏览器默认）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;443&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTTPS（浏览器默认）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;3000&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Node.js 开发服务器（Next.js、React、Create-React-App 默认）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;5173&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Vite 开发服务器默认端口&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;8080&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTTP 备用（Java/Tomcat、Nginx 代理常用）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;8000&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Python/Django、FastAPI 开发&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;5000&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Flask 默认&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;9000&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;SonarQube、php-fpm&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 data-id=&quot;heading-10&quot;&gt;🗄️ 数据库与缓存&lt;/h3&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;端口&lt;/th&gt;&lt;th&gt;数据库&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;3306&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;MySQL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;5432&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;PostgreSQL&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;6379&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Redis&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;27017&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;MongoDB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;1521&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Oracle&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;1433&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;SQL Server&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;7001&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Cassandra&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;9042&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Cassandra（CQL 原生协议）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 data-id=&quot;heading-11&quot;&gt;🔧 运维与基础设施&lt;/h3&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;端口&lt;/th&gt;&lt;th&gt;服务&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;22&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;SSH&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;3389&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;RDP（Windows 远程桌面）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;9090&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Prometheus&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;30000 ~ 32767&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Kubernetes NodePort 默认范围&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;建议把这张表存下来，配环境、排查问题时反复用。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-12&quot;&gt;五、端口的六大应用场景&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-13&quot;&gt;1️⃣ 开发调试&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# 前端开发&lt;/span&gt;
npm run dev -- --port 3000   &lt;span class=&quot;hljs-comment&quot;&gt;# Next.js&lt;/span&gt;
npm run dev -- --port 5173   &lt;span class=&quot;hljs-comment&quot;&gt;# Vite&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# 后端开发&lt;/span&gt;
node server.js                       &lt;span class=&quot;hljs-comment&quot;&gt;# 默认 3000&lt;/span&gt;
python manage.py runserver 0.0.0.0:8000   &lt;span class=&quot;hljs-comment&quot;&gt;# Django&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器访问 &lt;code&gt;http://localhost:3000&lt;/code&gt;，本质就是告诉操作系统：&quot;我要连本机的 3000 号端口。&quot;&lt;/p&gt;
&lt;h3 data-id=&quot;heading-14&quot;&gt;2️⃣ 生产环境部署&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-nginx&quot; lang=&quot;nginx&quot;&gt;# Nginx 监听 80，转发到后端 3000
server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;为什么生产用 80/443 而非 3000？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;浏览器访问 &lt;code&gt;http://example.com&lt;/code&gt; 时默认连 80，&lt;code&gt;https://example.com&lt;/code&gt; 默认连 443&lt;/li&gt;
&lt;li&gt;如果用 3000，用户必须输入 &lt;code&gt;http://example.com:3000&lt;/code&gt;，体验差&lt;/li&gt;
&lt;li&gt;最佳实践：Nginx 监听 80/443（需要 root），反向代理到内部服务的 3000 端口（非 root 运行更安全）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-id=&quot;heading-15&quot;&gt;3️⃣ 端口转发与 SSH 隧道&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# 把远程服务器的 3306 映射到本地 3306&lt;/span&gt;
ssh -L 3306:localhost:3306 user@remote-server

&lt;span class=&quot;hljs-comment&quot;&gt;# 之后访问 localhost:3306，实际连的是远程 MySQL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是本地开发连线上数据库最安全的方式——数据库端口不用暴露在公网。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-16&quot;&gt;4️⃣ 容器化部署（Docker）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot; lang=&quot;yaml&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;services:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;web:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;8080:3000&quot;&lt;/span&gt;   &lt;span class=&quot;hljs-comment&quot;&gt;# 宿主机 8080 → 容器 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问 &lt;code&gt;http://localhost:8080&lt;/code&gt;，实际访问容器内的 3000 端口。Docker 通过端口映射，让多个容器的 3000 可以共存，靠宿主机不同端口区分。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-17&quot;&gt;5️⃣ 微服务架构&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;API Gateway :80 → 按路径转发到：
  ├── user-service:3001
  ├── order-service:3002
  └── payment-service:3003
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个微服务绑不同端口，网关根据 URL 路径做路由。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-18&quot;&gt;6️⃣ 网络扫描与安全排查&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# 查看本机正在监听哪些端口&lt;/span&gt;
netstat -tlnp                          &lt;span class=&quot;hljs-comment&quot;&gt;# Linux&lt;/span&gt;
lsof -iTCP -sTCP:LISTEN -n -P          &lt;span class=&quot;hljs-comment&quot;&gt;# macOS&lt;/span&gt;
Get-NetTCPConnection | Where-Object State -EQ Listen   &lt;span class=&quot;hljs-comment&quot;&gt;# Windows PowerShell&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# 扫描远程主机开放了哪些端口&lt;/span&gt;
nmap example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;nmap&lt;/code&gt; 是黑客和运维都爱用的神器——前者用来找攻击面，后者用来确认服务是否正常暴露。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-19&quot;&gt;六、端口相关的四个核心概念&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-20&quot;&gt;1. 监听（Listen）&lt;/h3&gt;
&lt;p&gt;服务程序启动后，调用 &lt;code&gt;bind()&lt;/code&gt; + &lt;code&gt;listen()&lt;/code&gt;，在操作系统的端口上&quot;竖起耳朵&quot;等待连接。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-javascript&quot; lang=&quot;javascript&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// Node.js 监听 3000 端口&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;const&lt;/span&gt; http = &lt;span class=&quot;hljs-built_in&quot;&gt;require&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&#39;http&#39;&lt;/span&gt;);
http.&lt;span class=&quot;hljs-title function_&quot;&gt;createServer&lt;/span&gt;(&lt;span class=&quot;hljs-function&quot;&gt;(&lt;span class=&quot;hljs-params&quot;&gt;req, res&lt;/span&gt;) =&amp;gt;&lt;/span&gt; {
  res.&lt;span class=&quot;hljs-title function_&quot;&gt;end&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&#39;Hello&#39;&lt;/span&gt;);
}).&lt;span class=&quot;hljs-title function_&quot;&gt;listen&lt;/span&gt;(&lt;span class=&quot;hljs-number&quot;&gt;3000&lt;/span&gt;, &lt;span class=&quot;hljs-function&quot;&gt;() =&amp;gt;&lt;/span&gt; {
  &lt;span class=&quot;hljs-variable language_&quot;&gt;console&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;log&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&#39;Server listening on port 3000&#39;&lt;/span&gt;);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-id=&quot;heading-21&quot;&gt;2. 端口冲突&lt;/h3&gt;
&lt;p&gt;如果两个程序都想监听同一个端口，就会报错：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;Error: listen EADDRINUSE: address already in use :::3000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;解决方法&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# 杀掉占用 3000 端口的进程&lt;/span&gt;
lsof -ti:3000 | xargs &lt;span class=&quot;hljs-built_in&quot;&gt;kill&lt;/span&gt; -9

&lt;span class=&quot;hljs-comment&quot;&gt;# 或者换一个端口启动&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-id=&quot;heading-22&quot;&gt;3. 防火墙与端口&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;入站规则&lt;/strong&gt;：允许外部访问本机哪些端口&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出站规则&lt;/strong&gt;：允许本机访问外部哪些端口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;云服务器（AWS、阿里云、腾讯云）的&quot;安全组&quot;，本质就是端口级别的防火墙。很多新手部署完网站访问不了，十有八九是安全组没开 80/443。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-23&quot;&gt;4. &lt;code&gt;0.0.0.0&lt;/code&gt; vs &lt;code&gt;localhost&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-javascript&quot; lang=&quot;javascript&quot;&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;listen&lt;/span&gt;(&lt;span class=&quot;hljs-number&quot;&gt;3000&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;localhost&#39;&lt;/span&gt;)  &lt;span class=&quot;hljs-comment&quot;&gt;// 只能本机访问&lt;/span&gt;
.&lt;span class=&quot;hljs-title function_&quot;&gt;listen&lt;/span&gt;(&lt;span class=&quot;hljs-number&quot;&gt;3000&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;0.0.0.0&#39;&lt;/span&gt;)    &lt;span class=&quot;hljs-comment&quot;&gt;// 允许所有 IP 访问（容器/生产环境常用）&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;localhost&lt;/code&gt; / &lt;code&gt;127.0.0.1&lt;/code&gt;：仅本机回环，外部访问不到&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0.0.0.0&lt;/code&gt;：绑定到所有网络接口，局域网和公网都能连&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;🐛 Docker 容器里的服务一定要监听 &lt;code&gt;0.0.0.0&lt;/code&gt; 而不是 &lt;code&gt;localhost&lt;/code&gt;，否则宿主机和外部都访问不到，这是初学者高频踩坑点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-24&quot;&gt;七、端口与 URL 的关系&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;https://example.com:443/path?query=1
       │            │ │
       │            │ └─ 端口（省略时，https 默认 443，http 默认 80）
       │            └─ 域名
       └─ 协议
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器默认行为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://example.com&lt;/code&gt;  → 实际连接 &lt;code&gt;example.com:80&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com&lt;/code&gt; → 实际连接 &lt;code&gt;example.com:443&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果服务跑在其他端口，必须在 URL 中显式指定：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;http://example.com:3000
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-25&quot;&gt;八、一个完整的请求旅程（串联整个系列）&lt;/h2&gt;
&lt;p&gt;把 DNS、TCP、端口、HTTP、Nginx 全串起来，看看一次 &lt;code&gt;https://example.com/path&lt;/code&gt; 请求中端口到底起了什么作用：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;1. 浏览器输入 https://example.com:443/path
   ↓
2. DNS 解析 example.com → 93.184.216.34（第 15 篇）
   ↓
3. TCP 三次握手：本地随机端口(如 51023) ↔ 93.184.216.34:443（第 08 篇）
   ↓
4. TLS 握手（HTTPS 加密协商）
   ↓
5. 发送 HTTP 请求：GET /path HTTP/1.1（第 09 篇）
   ↓
6. 服务器操作系统收到数据包，看目标端口是 443
   ↓
7. 查端口映射表：443 被 Nginx 监听
   ↓
8. 数据交给 Nginx 进程
   ↓
9. Nginx 反向代理：把请求转发给 127.0.0.1:3000（Node.js）
   ↓
10. Node.js 应用处理，返回响应
   ↓
11. 响应沿原路返回：3000 → 443 → 客户端临时端口 51023
   ↓
12. 浏览器收到响应，渲染页面
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;端口在每一步都起着&quot;路由&quot;作用&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;客户端用临时端口标识自己&lt;/li&gt;
&lt;li&gt;服务器用 443 标识 HTTPS 服务&lt;/li&gt;
&lt;li&gt;内部用 3000 标识 Node.js 应用&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-26&quot;&gt;九、安全最佳实践&lt;/h2&gt;
&lt;h3 data-id=&quot;heading-27&quot;&gt;1. 最小化开放端口&lt;/h3&gt;
&lt;p&gt;只开放必要的端口，其余全部防火墙挡掉。端口开得越多，攻击面越大。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-28&quot;&gt;2. 不要在公网暴露数据库端口&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;❌ MySQL 3306、Redis 6379 直接暴露公网
✅ 只允许内网访问，或通过 SSH 隧道连接
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Redis 早年默认无密码且暴露公网，导致无数服务器被加密勒索，这个教训至今仍然有效。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-29&quot;&gt;3. 改掉默认端口（提高门槛）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;SSH 22 → 改为 2222 或其他高位端口
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这属于&quot;安全靠隐蔽&quot;（Security by Obscurity），不是真正的安全措施，但能挡住大部分自动化扫描和蠕虫攻击。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-30&quot;&gt;4. 使用非 root 用户运行服务&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;1024 以下的端口需要 root 权限才能绑定
生产环境用 1024 以上端口（如 3000、8080）跑业务
通过 Nginx 监听 80/443 做反向代理
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样即使业务进程被攻破，攻击者也拿不到 root 权限。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-31&quot;&gt;十、总结&lt;/h2&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;概念&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;端口是什么&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;0~65535 的整数，用于区分一台机器上的不同服务&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;端口 + IP&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;IP 定位机器，端口定位程序&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;80/443&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;HTTP/HTTPS 默认端口，浏览器自动补全&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;3000/5173/8080&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;开发服务器常用端口&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;3306/6379&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;MySQL/Redis 默认端口&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;三段分类&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;知名端口 (0-1023) / 注册端口 (1024-49151) / 动态端口 (49152-65535)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;TCP vs UDP&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;同一端口号在 TCP 和 UDP 上是独立的，互不影响&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;监听&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;程序通过 &lt;code&gt;bind + listen&lt;/code&gt; 在端口上等待连接&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;端口冲突&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;EADDRINUSE&lt;/code&gt;，杀掉占用进程或换端口&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-32&quot;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;端口是那种&quot;看似简单、实则贯穿全局&quot;的概念。开发时你敲 &lt;code&gt;npm run dev&lt;/code&gt; 看到的 &lt;code&gt;localhost:3000&lt;/code&gt;，部署时配的 Nginx &lt;code&gt;listen 80&lt;/code&gt;，排查问题时用的 &lt;code&gt;lsof -i:3306&lt;/code&gt;，云服务器上开的安全组规则——本质上都是在和端口打交道。&lt;/p&gt;
&lt;p&gt;理解了端口，你才能真正搞懂这些常见问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为什么我的服务启动了但外面访问不到？（没监听 &lt;code&gt;0.0.0.0&lt;/code&gt;，或防火墙没开端口）&lt;/li&gt;
&lt;li&gt;为什么 Docker 里要写 &lt;code&gt;&quot;8080:3000&quot;&lt;/code&gt;？（宿主机端口和容器端口的映射）&lt;/li&gt;
&lt;li&gt;为什么说数据库端口不能暴露公网？（3306/6379 一旦被扫到，就是攻击入口）&lt;/li&gt;
&lt;li&gt;为什么一个 IP 能同时跑网站、数据库、邮件？（不同端口分给不同进程）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;至此，网络通信系列已经覆盖了从 DNS 到端口、从 TCP 到 HTTP 的完整链路。下一篇我们讲代理，“中间人”位于客户端和服务器之间，转发请求和响应。&lt;/p&gt;
&lt;p&gt;如果这个系列对你有帮助，欢迎点赞、关注、收藏三连，我们下篇见 👋&lt;/p&gt;</description><link>https://juejin.cn/post/7671231304077901875</link><guid isPermaLink="false">https://juejin.cn/post/7671231304077901875</guid><pubDate>Sat, 08 Aug 2026 03:34:03 GMT</pubDate><author>颜进强</author><category>前端</category><category>后端</category><category>AI编程</category></item><item><title>会话选择器不是列表：Orca 如何守住切换边界</title><description>&lt;p&gt;很多终端 Agent 的会话选择器，看起来只是一个列表：上下移动，按 Enter 打开，按 Tab 看几个操作。真正容易出错的地方藏在“打开”之后：当前会话能不能删？Fork 之后屏幕上的历史是谁的？重命名写进磁盘后，运行中的标题会不会又变回去？&lt;/p&gt;
&lt;p&gt;我最近重读 Orca v0.3.8 的 TUI，会发现 session picker 更像一个小型状态机。列表只是入口，身份、投影和破坏性操作才是它真正要守的边界。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-0&quot;&gt;操作列表先看当前身份&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;available_session_actions&lt;/code&gt; 接收两个值：当前附着的 session id，以及用户选中的 session id。Resume、Fork、Rename 对两者都可用；只有选中的不是当前会话时，Archive 和 Delete 才会加入操作列表；最后再追加 Copy session ID。&lt;/p&gt;
&lt;p&gt;这段判断很短，却比在 controller 里收到 Delete 后再拒绝更可靠。用户根本看不到针对当前会话的删除入口，键盘上下移动的索引也只在真实可用的列表里计算。&lt;code&gt;current_session_actions_exclude_archive_and_delete&lt;/code&gt; 测试同时检查了两层：动作集合没有 Archive/Delete，连续按 Down 也不会越过最后一个合法动作。&lt;/p&gt;
&lt;p&gt;这里有一条值得带走的 UI 原则：破坏性动作的禁用状态要在动作生成处表达，controller 仍然可以保底拒绝，但不能把错误选项先交给用户。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-1&quot;&gt;Fork 不是打开旧会话&lt;/h2&gt;
&lt;p&gt;Resume 和 Fork 都从保存的 session 出发，运行时语义却不同。Resume 让当前 thread 接回原身份；Fork 则创建新身份，并把源会话作为 parent。切换完成后，TUI 必须同时更换运行时 owner 和屏幕上的 projection。&lt;/p&gt;
&lt;p&gt;Orca 的 &lt;code&gt;ForkSavedSession&lt;/code&gt; 分支现在先调用 &lt;code&gt;switch_saved_hosted_session&lt;/code&gt;，再轮换 attached event sender。随后发出 &lt;code&gt;SessionProjectionReset&lt;/code&gt;，清掉当前会话的消息、计划和状态；&lt;code&gt;announce_runtime_ready&lt;/code&gt; 重新报告新 thread 的身份；&lt;code&gt;emit_typed_history_snapshot&lt;/code&gt; 再把 fork 后的历史投影回来。最后才发 &lt;code&gt;SessionForked&lt;/code&gt;，让界面更新标题和 session id。&lt;/p&gt;
&lt;p&gt;顺序不能随意交换。只发一个 &lt;code&gt;SessionForked&lt;/code&gt; 事件，旧 transcript 可能还留在屏幕上；先绘制历史再换 owner，则旧 session 的后台事件可能继续写进新 projection。对应的 &lt;code&gt;picker_fork_replaces_source_transcript&lt;/code&gt; 测试明确验证：新会话里能看到 source-only prompt，看不到 current-only prompt，而且 fork id 同时不同于 source id 和原当前会话 id。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-2&quot;&gt;标题有两份，写入不是结束&lt;/h2&gt;
&lt;p&gt;重命名也有类似的双层状态。保存文件里的标题是 durable metadata，TUI 和 runtime surface 里的标题是当前 projection。Orca v0.3.8 的修复把 rename 接到带 revision 的 &lt;code&gt;SessionMetadataPatch::SetTitle&lt;/code&gt;：先确认读到的 revision 仍然匹配，再提交持久化变更，成功后发出 &lt;code&gt;SessionRenamed&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这解决的是一个很具体的回滚问题：如果只改磁盘，不更新 runtime projection，下一次 &lt;code&gt;announce_runtime_ready&lt;/code&gt; 仍可能从旧 snapshot 读标题，把用户刚改的名字重新覆盖掉。&lt;code&gt;hosted_tui_rename_updates_durable_title_and_projection_event&lt;/code&gt; 测试把 durable title、revision 和 projection event 放在一次流程里验证；写入失败时，另一个回归测试还要求运行时标题恢复到原值。&lt;/p&gt;
&lt;p&gt;把“改名成功”写成一次字符串赋值，会漏掉 revision、失败恢复和下一次 ready event。它其实是一条小型提交协议。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-3&quot;&gt;读代码时可以先画三条线&lt;/h2&gt;
&lt;p&gt;我现在看会话选择器，会先把同一个操作沿三条线展开：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;用户动作 -&amp;gt; 可用动作集合 -&amp;gt; controller dispatch
会话切换 -&amp;gt; 新 runtime owner -&amp;gt; projection reset/history snapshot
元数据写入 -&amp;gt; revision commit -&amp;gt; durable row + typed event
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一条线负责“用户能不能选”；第二条线负责“屏幕和会话是不是同一个”；第三条线负责“改动能不能在下一次恢复后继续成立”。这三条线都通过，picker 才不只是一个看起来能用的列表。&lt;/p&gt;
&lt;p&gt;Orca 的这组修复给我的启发很具体：TUI 里最危险的 bug，通常不是某个按键没有响应，而是动作、身份和投影在不同时间点各自向前走了一步。先把当前 session id 作为动作生成的输入，再用 reset 和 typed snapshot 管住切换，最后用 revision 把标题写回同一个 owner，很多“偶尔显示错了”的问题才有机会变成可测试的不变量。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;源码依据：&lt;code&gt;crates/orca-tui/src/session_picker_actions.rs&lt;/code&gt; L32-L56、L401-L430；&lt;code&gt;crates/orca-tui/src/app.rs&lt;/code&gt; L4102-L4151、L8339-L8381；&lt;code&gt;crates/orca-tui/src/types.rs&lt;/code&gt; L448-L459、L2610-L2629；修复提交 &lt;code&gt;c055cd955&lt;/code&gt;、&lt;code&gt;829b744b8&lt;/code&gt;。&lt;/em&gt;&lt;/p&gt;</description><link>https://juejin.cn/post/7671231304077885491</link><guid isPermaLink="false">https://juejin.cn/post/7671231304077885491</guid><pubDate>Sat, 08 Aug 2026 03:33:51 GMT</pubDate><author>echoVic</author><category>人工智能</category><category>Agent</category><category>AI编程</category></item><item><title>前端看后端 15：什么是 DNS？</title><description>&lt;blockquote&gt;
&lt;p&gt;专栏第十五篇，网络与协议篇第八章。上一篇我们聊了 CORS，搞懂了浏览器为什么要&quot;扣押&quot;你的响应。但在那之前，浏览器还得先找到服务器在哪——你输入 &lt;code&gt;google.com&lt;/code&gt;，它怎么知道该连哪台机器？毕竟计算机只认 IP 地址，可没人会背 &lt;code&gt;142.250.72.206&lt;/code&gt; 这种数字。这中间的翻译官，就是今天的主角：&lt;strong&gt;DNS（域名系统）&lt;/strong&gt;。它是整个网页访问之旅的&quot;第一块多米诺骨牌&quot;，也是面试高频考点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-0&quot;&gt;一、DNS 是什么？——互联网的电话簿&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;DNS = Domain Name System（域名系统）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;一句话：&lt;strong&gt;DNS 是互联网的&quot;电话簿&quot;&lt;/strong&gt;——把人类可读的域名（&lt;code&gt;google.com&lt;/code&gt;）翻译成机器可读的 IP 地址（&lt;code&gt;142.250.72.206&lt;/code&gt;）。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-1&quot;&gt;为什么需要它？&lt;/h3&gt;
&lt;p&gt;计算机在网络上通信，靠的是 &lt;strong&gt;IP 地址&lt;/strong&gt;（如 &lt;code&gt;192.168.1.1&lt;/code&gt; 或 IPv6 的 &lt;code&gt;2001:db8::1&lt;/code&gt;）。但人类记数字的能力实在堪忧：&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;人类喜欢&lt;/th&gt;&lt;th&gt;机器需要&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;google.com&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;142.250.72.206&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;baidu.com&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;110.242.68.66&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;bilibili.com&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;119.3.70.xxx&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;DNS 就是解决这个&quot;人机语言不通&quot;问题的翻译官。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 类比：DNS 就像手机通讯录。你点一下&quot;妈妈&quot;就能拨出去，不用背 &lt;code&gt;+86 138xxxx&lt;/code&gt;。通讯录帮你把名字翻译成号码，DNS 帮你把域名翻译成 IP。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-2&quot;&gt;二、DNS 解析的完整流程（面试必考）&lt;/h2&gt;
&lt;p&gt;当你在浏览器输入 &lt;code&gt;https://www.google.com&lt;/code&gt; 并回车，到拿到 IP 地址，中间经历了 &lt;strong&gt;8 个步骤&lt;/strong&gt;。这是面试最常考的内容，建议截图保存。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;1. 浏览器缓存 → 2. 系统缓存 → 3. 本机 hosts 文件
                                        ↓
4. 本地 DNS 服务器（LDNS，通常是你的路由器或运营商）
                                        ↓
5. 根 DNS 服务器（Root） → 告诉你 .com 在哪
                                        ↓
6. 顶级域 DNS（TLD，.com 服务器） → 告诉你 google.com 的权威 DNS 在哪
                                        ↓
7. 权威 DNS 服务器（Authoritative） → 返回 www.google.com 的 IP
                                        ↓
8. 浏览器拿到 IP，开始 TCP 握手 + TLS + HTTP 请求
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-id=&quot;heading-3&quot;&gt;逐步拆解&lt;/h3&gt;
&lt;h4 data-id=&quot;heading-4&quot;&gt;1️⃣ 浏览器缓存&lt;/h4&gt;
&lt;p&gt;浏览器会缓存 DNS 结果（Chrome 默认 60 秒到几分钟不等）。先查自己有没有，有就直接用。&lt;/p&gt;
&lt;h4 data-id=&quot;heading-5&quot;&gt;2️⃣ 系统缓存&lt;/h4&gt;
&lt;p&gt;浏览器没命中，就问操作系统。Windows 调用 &lt;code&gt;gethostbyname&lt;/code&gt;，Linux/Mac 读取 &lt;code&gt;/etc/resolv.conf&lt;/code&gt; 里配置的 DNS 服务器。操作系统自己也有一层缓存。&lt;/p&gt;
&lt;h4 data-id=&quot;heading-6&quot;&gt;3️⃣ hosts 文件&lt;/h4&gt;
&lt;p&gt;系统缓存也没有，查 &lt;code&gt;hosts&lt;/code&gt; 文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows：&lt;code&gt;C:\Windows\System32\drivers\etc\hosts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Linux/Mac：&lt;code&gt;/etc/hosts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是程序员最常用的&quot;本地 DNS 劫持&quot;手段：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;# 开发环境常用
127.0.0.1       localhost
192.168.1.100   api.dev.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-id=&quot;heading-7&quot;&gt;4️⃣ 本地 DNS 服务器（LDNS）&lt;/h4&gt;
&lt;p&gt;hosts 也没命中，请求才真正发出去，送到&lt;strong&gt;本地 DNS 服务器&lt;/strong&gt;（通常由你的 ISP 提供，如电信的 &lt;code&gt;114.114.114.114&lt;/code&gt;，或你手动配的 Google &lt;code&gt;8.8.8.8&lt;/code&gt;）。LDNS 自己也有缓存，如果有就直接返回——大部分情况下，DNS 查询在这一步就结束了。&lt;/p&gt;
&lt;h4 data-id=&quot;heading-8&quot;&gt;5️⃣ 根 DNS 服务器&lt;/h4&gt;
&lt;p&gt;LDNS 没缓存，就去找&lt;strong&gt;根服务器&lt;/strong&gt;。全球只有 &lt;strong&gt;13 组&lt;/strong&gt;根服务器（编号 A-M）。根服务器不直接解析域名，它只告诉你：&quot;.com 域的 TLD 服务器在这。&quot;&lt;/p&gt;
&lt;h4 data-id=&quot;heading-9&quot;&gt;6️⃣ 顶级域 DNS（TLD）&lt;/h4&gt;
&lt;p&gt;LDNS 拿着根服务器的指引，去找 &lt;code&gt;.com&lt;/code&gt; 的 TLD 服务器。TLD 查了查，告诉你：&quot;&lt;code&gt;google.com&lt;/code&gt; 的权威 DNS 是 &lt;code&gt;ns1.google.com&lt;/code&gt;，你去找它。&quot;&lt;/p&gt;
&lt;h4 data-id=&quot;heading-10&quot;&gt;7️⃣ 权威 DNS 服务器&lt;/h4&gt;
&lt;p&gt;LDNS 终于找到 &lt;code&gt;google.com&lt;/code&gt; 的权威 DNS，这里才真正返回 &lt;code&gt;www.google.com&lt;/code&gt; 的 IP 地址（&lt;code&gt;142.250.72.206&lt;/code&gt;）。&lt;strong&gt;权威 DNS 是域名所有者自己配置的&lt;/strong&gt;，你在阿里云/Cloudflare 买的域名，那里配的记录就存在权威 DNS 上。&lt;/p&gt;
&lt;h4 data-id=&quot;heading-11&quot;&gt;8️⃣ 返回并缓存&lt;/h4&gt;
&lt;p&gt;LDNS 把结果返回给你的电脑，并按 TTL 缓存起来。浏览器也开始缓存，接下来就可以进入 TCP 握手、TLS 握手、发 HTTP 请求了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;⏱️ &lt;strong&gt;耗时&lt;/strong&gt;：整个过程通常在 &lt;strong&gt;几十毫秒到几百毫秒&lt;/strong&gt;。如果 LDNS 有缓存，甚至 &amp;lt; 1ms，你根本感知不到。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-12&quot;&gt;三、DNS 记录类型（重点）&lt;/h2&gt;
&lt;p&gt;DNS 不只是存 A 记录（域名→IP），它有十几种记录类型。作为工程师，这几种必须认识：&lt;/p&gt;




























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;记录类型&lt;/th&gt;&lt;th&gt;作用&lt;/th&gt;&lt;th&gt;例子&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;A&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;域名 → IPv4 地址&lt;/td&gt;&lt;td&gt;&lt;code&gt;google.com → 142.250.72.206&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;AAAA&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;域名 → IPv6 地址&lt;/td&gt;&lt;td&gt;&lt;code&gt;google.com → 2607:f8b0:4005:...&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CNAME&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;域名别名 → 另一个域名&lt;/td&gt;&lt;td&gt;&lt;code&gt;www.google.com → www.l.google.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;MX&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;邮件服务器&lt;/td&gt;&lt;td&gt;&lt;code&gt;google.com → smtp.google.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;NS&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;指定该域名的权威 DNS&lt;/td&gt;&lt;td&gt;&lt;code&gt;google.com → ns1.google.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;TXT&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;文本记录（SPF、域名验证）&lt;/td&gt;&lt;td&gt;&lt;code&gt;google.com → &quot;v=spf1 include:_spf.google.com ~all&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;PTR&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;IP → 域名（反向解析）&lt;/td&gt;&lt;td&gt;&lt;code&gt;8.8.8.8 → dns.google&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SOA&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;区域起始授权记录&lt;/td&gt;&lt;td&gt;包含主 DNS、管理员邮箱、刷新时间等&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SRV&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;服务定位记录&lt;/td&gt;&lt;td&gt;&lt;code&gt;_xmpp-server._tcp.gmail.com&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;CAA&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;指定哪些 CA 可以签发证书&lt;/td&gt;&lt;td&gt;&lt;code&gt;google.com → 0 issue &quot;pki.goog&quot;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 data-id=&quot;heading-13&quot;&gt;最常用的三类&lt;/h3&gt;
&lt;h4 data-id=&quot;heading-14&quot;&gt;1. A 记录（Address）&lt;/h4&gt;
&lt;p&gt;最基础的映射：域名 → IPv4。配网站、配接口，第一个要加的就是 A 记录。&lt;/p&gt;
&lt;h4 data-id=&quot;heading-15&quot;&gt;2. CNAME（Canonical Name）&lt;/h4&gt;
&lt;p&gt;把一个域名指向&lt;strong&gt;另一个域名&lt;/strong&gt;（注意：不能直接指向 IP）。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;images.google.com  CNAME  www.l.google.com
www.l.google.com   A      142.250.72.206
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;好处&lt;/strong&gt;：当 &lt;code&gt;www.l.google.com&lt;/code&gt; 的 IP 变了，&lt;code&gt;images.google.com&lt;/code&gt; 自动跟着变，不用改两条记录。CDN 厂商最喜欢让你 CNAME 到他们的域名，就是这个原因。&lt;/p&gt;
&lt;h4 data-id=&quot;heading-16&quot;&gt;3. MX 记录（Mail Exchange）&lt;/h4&gt;
&lt;p&gt;指定邮件服务器。发邮件到 &lt;code&gt;alice@gmail.com&lt;/code&gt; 时，发件方 DNS 查 &lt;code&gt;gmail.com&lt;/code&gt; 的 MX 记录，找到 Gmail 的邮件服务器，然后投递。MX 记录还能带优先级（数字越小越高）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-17&quot;&gt;四、DNS 的应用场景（重点）&lt;/h2&gt;
&lt;p&gt;DNS 远不止&quot;把域名变 IP&quot;这么简单，它在现代架构中扮演着&lt;strong&gt;核心调度角色&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-18&quot;&gt;1️⃣ 网站访问（基础场景）&lt;/h3&gt;
&lt;p&gt;最经典的用法。用户访问 &lt;code&gt;taobao.com&lt;/code&gt;，DNS 返回淘宝服务器的 IP，浏览器才能建立连接。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-19&quot;&gt;2️⃣ CDN 调度（智能解析）&lt;/h3&gt;
&lt;p&gt;CDN 厂商（Cloudflare、阿里云 CDN）通过 DNS 实现&lt;strong&gt;就近接入&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;用户在北京 → DNS 返回 北京节点的 IP
用户在上海 → DNS 返回 上海节点的 IP
用户在国外 → DNS 返回 美国节点的 IP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就是所谓的 &lt;strong&gt;GeoDNS&lt;/strong&gt;，根据用户的地理位置返回最近的 CDN 边缘节点 IP。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-20&quot;&gt;3️⃣ 负载均衡（DNS Round Robin）&lt;/h3&gt;
&lt;p&gt;一个域名对应多个 IP，DNS 轮询返回：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;api.example.com  A  10.0.0.1
api.example.com  A  10.0.0.2
api.example.com  A  10.0.0.3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每次 DNS 查询，DNS 服务器按顺序返回不同的 IP，实现简单的负载均衡。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;缺点&lt;/strong&gt;也很明显：DNS 缓存会导致流量分布不均；一台机器挂了，DNS 还会继续返回它的 IP。所以 DNS 轮询通常只作为第一层负载均衡，后面还要配合 Nginx/LVS 等。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-21&quot;&gt;4️⃣ 服务发现（微服务）&lt;/h3&gt;
&lt;p&gt;微服务架构中，服务实例动态变化，IP 随时在变。DNS 可以作为服务发现机制：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;user-service.default.svc.cluster.local → 10.244.1.5
user-service.default.svc.cluster.local → 10.244.2.7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Kubernetes 就用 CoreDNS 做服务发现，Service 名 → ClusterIP 的解析全靠它。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-22&quot;&gt;5️⃣ 高可用与故障转移&lt;/h3&gt;
&lt;p&gt;通过降低 TTL，当主服务器宕机时，快速修改 DNS 记录指向备用服务器。但因为各级缓存的存在，切换不是秒级的，通常有几分钟延迟。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-23&quot;&gt;6️⃣ 邮件系统&lt;/h3&gt;
&lt;p&gt;MX 记录指定邮件服务器，SPF/DKIM/DMARC 通过 TXT 记录验证邮件合法性，防止伪造和钓鱼。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-24&quot;&gt;7️⃣ 内网服务命名&lt;/h3&gt;
&lt;p&gt;公司内部用 DNS 代替记 IP：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;mysql.internal   → 10.0.0.100
redis.internal   → 10.0.0.101
jenkins.internal → 10.0.0.102
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没人愿意背 IP，内网 DNS 是提升幸福感的基础设施。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-25&quot;&gt;五、DNS 的 TTL（生存时间）&lt;/h2&gt;
&lt;p&gt;每条 DNS 记录都有一个 TTL 值（单位：秒），告诉缓存服务器&quot;这个结果多久内有效&quot;。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;www.example.com.  300  IN  A  93.184.216.34
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TTL=300 秒（5 分钟）意味着：LDNS 缓存 5 分钟，期间所有对这个域名的查询都直接返回这个 IP，不再去问权威 DNS。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TTL 的权衡&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TTL 短&lt;/strong&gt;（如 60 秒）：故障转移快，但 LDNS 查询频率高，解析慢，权威 DNS 压力大。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TTL 长&lt;/strong&gt;（如 86400 秒 = 1 天）：解析快，缓存命中率高，但 IP 变更后要等缓存过期，切换慢。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：正常情况下 TTL 设较长（如 3600 秒 = 1 小时）；变更前临时调短（如 60 秒）；变更完成后再调长。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-26&quot;&gt;六、DNS 的安全与隐私&lt;/h2&gt;
&lt;p&gt;DNS 从设计之初就没考虑安全，所以这些年补了不少补丁。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-27&quot;&gt;1. DNS 污染（DNS Spoofing）&lt;/h3&gt;
&lt;p&gt;攻击者伪造 DNS 响应，让你访问假网站（比如把 &lt;code&gt;taobao.com&lt;/code&gt; 指到钓鱼站点）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;防御&lt;/strong&gt;：&lt;strong&gt;DNSSEC&lt;/strong&gt;（DNS Security Extensions）给 DNS 记录加数字签名，客户端可以验证响应是否被篡改。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-28&quot;&gt;2. DNS 劫持&lt;/h3&gt;
&lt;p&gt;运营商或中间人篡改 DNS 查询结果，强行跳转到广告页。你有没有过访问一个网站却跳到运营商导航页的经历？那就是 DNS 劫持。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;防御&lt;/strong&gt;：&lt;strong&gt;DoH&lt;/strong&gt;（DNS over HTTPS）或 &lt;strong&gt;DoT&lt;/strong&gt;（DNS over TLS），把 DNS 查询也加密，中间人就看不到也改不了了。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-29&quot;&gt;3. 隐私泄露&lt;/h3&gt;
&lt;p&gt;传统 DNS 查询是明文的，ISP（或任何网络中间人）能看到你访问了哪些网站。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DoH&lt;/strong&gt;：DNS over HTTPS，走 443 端口，和普通 HTTPS 流量混在一起。Firefox、Chrome 都支持。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DoT&lt;/strong&gt;：DNS over TLS，走 853 端口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS over QUIC&lt;/strong&gt;：新兴方案，基于 QUIC 协议，性能更好。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-30&quot;&gt;七、公共 DNS 服务&lt;/h2&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;服务商&lt;/th&gt;&lt;th&gt;IPv4&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;Google Public DNS&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;8.8.8.8&lt;/code&gt; / &lt;code&gt;8.8.4.4&lt;/code&gt;&lt;/td&gt;&lt;td&gt;全球最快之一&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Cloudflare&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;1.1.1.1&lt;/code&gt;&lt;/td&gt;&lt;td&gt;注重隐私，支持 DoH/DoT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;国内 114&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;114.114.114.114&lt;/code&gt;&lt;/td&gt;&lt;td&gt;国内速度快&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;阿里 DNS&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;223.5.5.5&lt;/code&gt; / &lt;code&gt;223.6.6.6&lt;/code&gt;&lt;/td&gt;&lt;td&gt;国内稳定&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;OpenDNS&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;208.67.222.222&lt;/code&gt;&lt;/td&gt;&lt;td&gt;有内容过滤&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;开发调试常用&lt;/strong&gt;：&lt;code&gt;8.8.8.8&lt;/code&gt; 或 &lt;code&gt;114.114.114.114&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-31&quot;&gt;八、常用命令与工具&lt;/h2&gt;
&lt;p&gt;排查 DNS 问题时，这些命令是你的好帮手。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-32&quot;&gt;1. &lt;code&gt;nslookup&lt;/code&gt;（简单查询，跨平台）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;nslookup google.com
nslookup -&lt;span class=&quot;hljs-built_in&quot;&gt;type&lt;/span&gt;=MX google.com       &lt;span class=&quot;hljs-comment&quot;&gt;# 查 MX 记录&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-id=&quot;heading-33&quot;&gt;2. &lt;code&gt;dig&lt;/code&gt;（详细查询，Linux/Mac）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;dig google.com                     &lt;span class=&quot;hljs-comment&quot;&gt;# 查 A 记录&lt;/span&gt;
dig +trace google.com              &lt;span class=&quot;hljs-comment&quot;&gt;# 追踪完整解析链（根→TLD→权威）&lt;/span&gt;
dig @8.8.8.8 google.com            &lt;span class=&quot;hljs-comment&quot;&gt;# 指定 DNS 服务器&lt;/span&gt;
dig -t AAAA google.com             &lt;span class=&quot;hljs-comment&quot;&gt;# 查 IPv6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;dig +trace&lt;/code&gt; 能让你亲眼看到解析是怎么一步步从根服务器走到权威 DNS 的，强烈建议自己试一次。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-34&quot;&gt;3. &lt;code&gt;host&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;host google.com
host -t MX google.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-id=&quot;heading-35&quot;&gt;4. &lt;code&gt;ping&lt;/code&gt;（间接测试）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot; lang=&quot;bash&quot;&gt;ping google.com   &lt;span class=&quot;hljs-comment&quot;&gt;# 先触发 DNS 解析，再发 ICMP 包&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 &lt;code&gt;ping&lt;/code&gt; 一个域名能通但 &lt;code&gt;curl&lt;/code&gt; 不通，至少说明 DNS 解析没问题。&lt;/p&gt;
&lt;h3 data-id=&quot;heading-36&quot;&gt;5. 在线工具&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;whatsmydns.net&lt;/strong&gt;：全球 DNS 传播查询，改完记录后看各地是否生效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;dnschecker.org&lt;/strong&gt;：多节点 DNS 验证。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-37&quot;&gt;九、串联整个通信系列&lt;/h2&gt;
&lt;p&gt;到这里，我们终于可以把前面所有知识串成一条完整的&quot;网页访问之旅&quot;了：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot; lang=&quot;text&quot;&gt;1. 用户在浏览器输入 https://www.google.com
   ↓
2. DNS 解析（本篇主角）
   - 浏览器缓存 → 系统缓存 → hosts → LDNS → 根 → TLD → 权威 DNS
   - 拿到 IP: 142.250.72.206
   ↓
3. TCP 三次握手（第 08 篇 TCP/IP）
   ↓
4. TLS 握手（HTTPS 加密协商）
   ↓
5. HTTP 请求发送（第 09 篇 HTTP / 第 10 篇 RESTful）
   - 如果是跨域，还会有 CORS 预检（第 14 篇 CORS）
   ↓
6. 服务器处理
   - 检查 Cookie/Session 验证身份（第 13 篇）
   - 可能调用内部 gRPC 服务（第 11 篇）
   - 或查询数据库、缓存
   ↓
7. HTTP 响应返回
   ↓
8. 浏览器渲染页面
   - 如果页面有 WebSocket，会升级连接（第 12 篇）
   - 如果页面有 API 调用，又会回到第 2 步
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;DNS 是整个过程的&quot;第一块多米诺骨牌&quot;&lt;/strong&gt;——它不倒，后面全玩完。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-38&quot;&gt;十、总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DNS 是什么&lt;/strong&gt;：互联网的电话簿，域名 ↔ IP 的翻译系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解析流程&lt;/strong&gt;：浏览器缓存 → 系统缓存 → hosts → LDNS → 根 → TLD → 权威 DNS（8 步，面试必背）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心记录&lt;/strong&gt;：A（IPv4）、AAAA（IPv6）、CNAME（别名）、MX（邮件）、NS（权威）、TXT（文本验证）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：网站访问、CDN 智能调度、负载均衡、K8s 服务发现、邮件系统、高可用切换。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TTL&lt;/strong&gt;：平衡解析速度与变更灵活性的关键参数，变更前调短、变更后调长。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全&lt;/strong&gt;：DNSSEC 防污染，DoH/DoT 防劫持和保护隐私。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调试工具&lt;/strong&gt;：&lt;code&gt;dig&lt;/code&gt;、&lt;code&gt;nslookup&lt;/code&gt;、&lt;code&gt;host&lt;/code&gt;、&lt;code&gt;ping&lt;/code&gt;，以及 whatsmydns.net。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 data-id=&quot;heading-39&quot;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;DNS 是那种&quot;日用而不知&quot;的基础设施——你每天上网几百次，每次都要用它，但很少有人能完整说清解析流程。理解它最大的价值，不只是应付面试，而是当你遇到这些问题时不再抓瞎：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为什么改了 DNS 记录，有的用户立刻生效、有的要等半天？（TTL + 各级缓存）&lt;/li&gt;
&lt;li&gt;为什么配了 CDN 还是慢？（GeoDNS 调度错了节点）&lt;/li&gt;
&lt;li&gt;为什么域名能 ping 通但浏览器打不开？（可能是 hosts 被改了，或 DNS 被劫持）&lt;/li&gt;
&lt;li&gt;为什么 Kubernetes 里 Service 名能直接当域名用？（CoreDNS 在做服务发现）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下一篇，我们来聊一个你天天在配、却未必真正理解的概念：&lt;strong&gt;什么是端口？一台服务器只有一个 IP，为什么能同时跑网站、数据库、邮件那么多服务？80、443、3306、8080 这些数字背后到底是什么？端口和进程又是怎么对应起来的？&lt;/strong&gt; 敬请期待。&lt;/p&gt;
&lt;p&gt;如果这个系列对你有帮助，欢迎点赞、关注、收藏三连，我们下篇见 👋&lt;/p&gt;</description><link>https://juejin.cn/post/7671474795013406729</link><guid isPermaLink="false">https://juejin.cn/post/7671474795013406729</guid><pubDate>Sat, 08 Aug 2026 03:29:06 GMT</pubDate><author>颜进强</author><category>前端</category><category>后端</category><category>AI编程</category></item><item><title>15天学会AI应用开发（十九）使用LangGraph实现持久记忆功能</title><description>&lt;p&gt;上一篇文章介绍了如何使用InMemoryStore实现用户偏好的精确记忆功能，但记忆只保留在内存中，一旦AI应用重启，就会丢失上次在内存中的记忆数据。&lt;/p&gt;
&lt;p&gt;为了让AI应用持久记忆业务数据，就得引入文件或者数据库等存储介质，比如Python自带的轻量级数据库SQLite。接下来就介绍如何结合LangGraph与SQLite实现业务数据的持久记忆功能。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-0&quot;&gt;一、Python中的SQLite用法&lt;/h2&gt;
&lt;p&gt;安装Python时已经自带了SQLite包，在代码开头添加下面导包语句，即可引入SQLite包：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-arduino&quot; lang=&quot;arduino&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; sqlite3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sqlite3的connect方法可创建SQLite的数据库连接，同时指定数据库的文件名称及其保存路径。若只填数据库文件名，则默认保存在当前程序目录下。&lt;br&gt;
connect方法返回的连接类型为Connection，它的常用方法说明如下：&lt;/p&gt;
&lt;p&gt;backup：备份数据库。&lt;br&gt;
close：关闭数据库连接。&lt;br&gt;
commit：提交数据变更。&lt;br&gt;
rollback：回滚数据变更。&lt;/p&gt;
&lt;h2 data-id=&quot;heading-1&quot;&gt;二、LangGraph中的SQLite组件&lt;/h2&gt;
&lt;p&gt;LangGraph中的SQLite组件包括SqliteStore和SqliteSaver，其中SqliteStore位于langgraph包，而SqliteSaver位于langgraph-checkpoint-sqlite插件。&lt;/p&gt;
&lt;p&gt;在命令行窗口执行下面命令，即可安装LangChain及其用到的Ollama插件，以及数据库记忆功能需要的LangGraph及其SQLite插件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;pip install langchain langchain-core langchain-ollama langgraph langgraph-checkpoint-sqlite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于之前使用的离线大模型Qwen2:1.5B不支持tool工具调用，因此得下载支持tool工具调用的离线大模型Qwen2.5:3B，也就是在命令行窗口执行下面命令，即可下载大模型qwen2.5:3b：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;ollama pull qwen2.5:3b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;等待大模型下载完毕，在命令行窗口执行下面命令，即可启动Ollama并加载大模型：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-id=&quot;heading-2&quot;&gt;三、使用SQLite组件持久保存偏好数据&lt;/h2&gt;
&lt;p&gt;与之前使用InMemoryStore保存记忆数据相比，引入SQLite插件之后的代码修改部分说明如下：&lt;/p&gt;
&lt;p&gt;1、先导入SQLite状态保存器SqliteSaver和SQLite数据保存器SqliteStore的所在包，接着创建SQLite数据库连接，再根据数据库连接分别创建状态保存器实例和数据保存器实例。示例代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ini&quot; lang=&quot;ini&quot;&gt;import sqlite3
&lt;span class=&quot;hljs-comment&quot;&gt;# 使用SqliteStore替换InMemoryStore&lt;/span&gt;
from langgraph.store.sqlite import SqliteStore
from langgraph.checkpoint.sqlite import SqliteSaver

&lt;span class=&quot;hljs-comment&quot;&gt;# 创建数据库连接&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;check_conn&lt;/span&gt;= sqlite3.connect(&lt;span class=&quot;hljs-string&quot;&gt;&quot;agent.db&quot;&lt;/span&gt;, check_same_thread=&lt;span class=&quot;hljs-literal&quot;&gt;False&lt;/span&gt;, isolation_level=None)
&lt;span class=&quot;hljs-comment&quot;&gt;# 状态保存器，保存对话线程运行状态，自动建 checkpoints / writes 表&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;checkpointer&lt;/span&gt; = SqliteSaver(check_conn)
&lt;span class=&quot;hljs-comment&quot;&gt;# 创建数据库连接&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;store_conn&lt;/span&gt;= sqlite3.connect(&lt;span class=&quot;hljs-string&quot;&gt;&quot;agent.db&quot;&lt;/span&gt;, check_same_thread=&lt;span class=&quot;hljs-literal&quot;&gt;False&lt;/span&gt;, isolation_level=None)
&lt;span class=&quot;hljs-comment&quot;&gt;# 数据保存器，保存用户事实 / 工具记忆，自动建 store_namespaces / store_items 表&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;store_db&lt;/span&gt; = SqliteStore(store_conn)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、在创建大模型实例时，既要将温度参数temperature设为0表示严格按照提示词执行，也要将工具选择参数tool_choice设为any表示强制模型必须调用工具。示例代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ini&quot; lang=&quot;ini&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;llm&lt;/span&gt; = ChatOllama(
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;model&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;qwen2.5:3b&quot;&lt;/span&gt;,
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;temperature&lt;/span&gt;=&lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;, &amp;nbsp;&lt;span class=&quot;hljs-comment&quot;&gt;# 0温度，消除随机胡思乱想，严格按prompt执行&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;num_ctx&lt;/span&gt;=&lt;span class=&quot;hljs-number&quot;&gt;8192&lt;/span&gt;
&amp;nbsp; &amp;nbsp; )
&lt;span class=&quot;hljs-comment&quot;&gt;# 关键：提前绑定工具+强制工具策略&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;tools_list&lt;/span&gt; = [remember_user_preference, get_user_preference]
&lt;span class=&quot;hljs-comment&quot;&gt;# tool_choice=&quot;any&quot; 等价required，强制模型必须输出工具调用&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;llm_temp&lt;/span&gt; = llm.bind_tools(tools_list, tool_choice=&lt;span class=&quot;hljs-string&quot;&gt;&quot;any&quot;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、在调用create_agent创建智能体实例时，把store参数改为上一步创建的数据保存器实例store_db，把checkpointer参数改为上一步创建的状态保存器实例checkpointer；还要编写提示词参数system_prompt，指定详细的工具调用规则。示例代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ini&quot; lang=&quot;ini&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# 用户提到喜欢什么东西时，会调用remember_user_preference工具；用户询问喜欢什么东西时，会调用get_user_preference工具。&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;agent&lt;/span&gt; = create_agent(
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;model&lt;/span&gt;=llm,
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;context_schema&lt;/span&gt;=Context,
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;tools&lt;/span&gt;=tools_list,
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;store&lt;/span&gt;=store_db,
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;checkpointer&lt;/span&gt;=checkpointer,
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;system_prompt&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&quot;
&amp;nbsp; &amp;nbsp; 你是用户记忆助手，严格遵守以下规则：
&amp;nbsp; &amp;nbsp; 1. 只要用户主动说出自己的喜好、偏好（比如喜欢的颜色、食物、风格），**无论之前是否存储过同类偏好，就必须调用 remember_user_preference 工具更新覆盖**，不能跳过工具直接回答；
&amp;nbsp; &amp;nbsp; 2. 只要用户主动询问自己的偏好，就调用 get_user_preference 工具查询；
&amp;nbsp; &amp;nbsp; 3. 禁止省略存储工具：用户修改原有喜好，必须调用工具更新数据；
&amp;nbsp; &amp;nbsp; 4. 工具调用完成后，再向用户自然回复。
&amp;nbsp; &amp;nbsp; &quot;&quot;&quot;&lt;/span&gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4、在调用智能体实例的invoke方法时，增加传入config参数，指定SQLite组件所需的线程编号。示例代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ini&quot; lang=&quot;ini&quot;&gt;&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-comment&quot;&gt;# 调用离线大模型&lt;/span&gt;
&amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;result&lt;/span&gt; = agent.invoke(
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {&quot;messages&quot;: &lt;span class=&quot;hljs-section&quot;&gt;[HumanMessage(content=user_input)]&lt;/span&gt;},
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;context&lt;/span&gt;=Context(user_id=&lt;span class=&quot;hljs-string&quot;&gt;&quot;user_123&quot;&lt;/span&gt;),
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span class=&quot;hljs-attr&quot;&gt;config&lt;/span&gt;={&lt;span class=&quot;hljs-string&quot;&gt;&quot;configurable&quot;&lt;/span&gt;: {&lt;span class=&quot;hljs-string&quot;&gt;&quot;thread_id&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;session-001&quot;&lt;/span&gt;}}
&amp;nbsp; &amp;nbsp; )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行修改后的Python代码，根据提示输入问题内容“我喜欢蓝色”，输出日志结果如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;用户：我喜欢蓝色
AI：我已经记住了，你现在喜欢的颜色是蓝色。如果你有其他颜色的偏好或者想要改变现有的偏好，请告诉我！
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;停止当前AI应用进程，重新运行Python代码，根据提示输入问题内容“我喜欢什么颜色？”，输出日志结果如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;用户：我喜欢什么颜色？
AI：您现在最喜欢的颜色是蓝色。如果有其他问题或需要进一步帮助，请随时告诉我！
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由日志信息发现，在应用程序重启前后，AI应用均能正确回答用户的颜色偏好，并且当前目录找到了数据库文件agent.db。可见在引入LangGraph的SQLite组件之后，LangChain成功实现了用户数据的持久记忆功能。&lt;/p&gt;
&lt;p&gt;本系列的AI应用开发文章目录为《15天学会AI应用开发全目录（零基础小白，零Token消耗）》。&lt;/p&gt;</description><link>https://juejin.cn/post/7671157586478383119</link><guid isPermaLink="false">https://juejin.cn/post/7671157586478383119</guid><pubDate>Sat, 08 Aug 2026 03:28:28 GMT</pubDate><author>aqi00</author><category>人工智能</category><category>Python</category><category>AI编程</category></item></channel></rss>