<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Think In Python</title>
        <link>http://www.thinkinpython.com</link>
        <description>Think in Python, Programming better.</description>
        <atom:link href="http://www.thinkinpython.com/rss.html" rel="self" />
        <language>zh-cn</language>
        <lastBuildDate>Fri, 05 Jun 2026 18:03:49 GMT</lastBuildDate>
        <item>
            <title>关于光连接，华尔街不会告诉你的事</title>
            <link>http://www.thinkinpython.com/post/optical_link_in_gpu.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-456">一、 账本里的真相</a></li>
<li><a href="#toc-748">二、 机架内互联: 被高估的“全光”刚需</a></li>
<li><a href="#toc-2b9">三、 集群光互连: 缺乏爆发逻辑</a></li>
<li><a href="#toc-25f">总结</a></li>
</ul>
</div><p>在科技投资的宏大叙事里，华尔街最擅长的就是为每一个微小的技术迭代包装出数千亿美元的“总体可寻址市场（TAM）”。在这一轮 AI 算力狂欢中，“光连接（Optical Interconnect）”无疑是被聚光灯打得最足的概念之一。从“算力爆发必带来光模块无处不在”到“CPO技术重塑数据中心”，激进的研报不断刺激着投资者的神经。</p>
<p>然而，剥开这些被精心包裹的幻象，回到最底层的服务器成本拆解（BOM）与物理工程现实，我们会发现一个被刻意忽略的真相：光连接概念正面临着严重的过度炒作。在 AI 硬件的权力版图中，它只是一个缺乏溢价资本的边缘角色。</p>
<hr>
<h2><a id="toc-456" class="anchor" href="#toc-456"></a>一、 账本里的真相</h2>
<p>AI 硬件的绝对核心只有 GPU 和 HBM
华尔街乐于展示光连接设备出货量的同比翻倍增长，但他们很少会让你看一眼一台标准 AI 服务器的真实成本构成。</p>
<p>以目前全球数据中心标配的 8 卡 NVIDIA H200 服务器为例，其整机建议零售价（MSRP）约为 $350,000。当我们把这张账单拆细，其价值链的真实垄断格局一目了然：</p>
<p><img src="https://thinkinpython.com/static/upload/20260605/upload_8e0f826ad0cb13b10acc079faad8e7fc.png" alt="image_900d1719.png"></p>
<p><img src="https://thinkinpython.com/static/upload/20260605/upload_c1f9488e2b0fc37484235a1e6531bb82.png" alt="iShot_2026-06-05_18.24.16.png"></p>
<ul>
<li>绝对核心：8 块配置了 141GB HBM3e 显存的 H200 GPU 核心采购成本高达 $256,000，直接吞噬了整机 70% 的资金。</li>
<li>边缘分润：包含了高速网卡、光通信接口在内的整个网络与 I/O 组件，在单机整机中的采购成本仅占 7%。</li>
</ul>
<p>在 AI 硬件中，任何不能直接贡献算力（FLOPS）或显存带宽（HBM）的组件，都是外围设备。英伟达凭借核心芯片与 CUDA 生态卷走了全产业链近一半的纯利润，而光连接设备在整机中只是极其边缘的硬件，由于缺乏生态护城河，其价值根本不存在爆发的基础。</p>
<hr>
<h2><a id="toc-748" class="anchor" href="#toc-748"></a>二、 机架内互联: 被高估的“全光”刚需</h2>
<p>华尔街的叙事逻辑是线性外推：速度越快，就越需要光。但他们没有告诉你的是，在短距离互联中，铜连接已经取得巨大突破：</p>
<ul>
<li><p>目前基于纯铜互联的 NVLink 6 已经能够实现单卡双工 3.6TB/s、机架级一二百 TB/s 的惊人带宽，至少在目前已经相当够用。</p>
</li>
<li><p>光通信听起来快，但它必须在传输时进行 2 次光电转换，这个过程会引入不可避免的物理延时，光电转换也有能耗、发热问题。铜连接工艺成熟、技术稳定、直接传电信号，近乎零延迟，性价比极高。</p>
</li>
</ul>
<p>对于下游客户（如云服务商）而言，机架内通过现有的铜互联方案已经足够快、足够稳。这就像当年的 5G 概念和 IPv6 升级一样——技术指标固然完美，但在老的 4G 和 IPv4 依然够用且极其省钱的背景下，用户缺乏迫切的升级动力。 </p>
<hr>
<h2><a id="toc-2b9" class="anchor" href="#toc-2b9"></a>三、 集群光互连: 缺乏爆发逻辑</h2>
<p>当然，不能否认光连接的价值。在超大集群的构建中，光连接具有铜缆无法替代的优势：</p>
<ul>
<li>打破距离限制：当万卡、十万卡集群需要跨机柜、跨机房甚至跨建筑连接时，铜缆在超过 2-3 米后信号会发生剧烈的物理衰减。此时，必须通过光纤通信来突破距离限制。</li>
<li>但这不会造就价值爆发：光连接在多机柜集群中确实有刚性场景，但它无法改变其在整体数据中心资本开支（CapEx）中仅占个位数的边缘地位。更重要的，这类光通信设备缺乏像核心算力芯片那样的技术专利垄断和软件生态锁死，随着代工厂规模化量产与良率提升，其 ASP（平均售价）必然被下游客户持续压低。</li>
</ul>
<h2><a id="toc-25f" class="anchor" href="#toc-25f"></a>总结</h2>
<p>华尔街制造“光连接”的增长神话，过度炒作光连接概念。我们必须看清底层的硬核逻辑：AI 硬件的绝对重心始终在核心芯片（GPU）与高带宽显存（HBM）身上。光连接在 AI 硬件中必然有一席之地，但也仅仅是一席之地，不会成为下一个英伟达。</p>

            ]]></description>
            <pubDate>Thu, 04 Jun 2026 15:12:35 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/optical_link_in_gpu.html</guid>
        </item>
        <item>
            <title>悖论——AI 正在反噬英伟达</title>
            <link>http://www.thinkinpython.com/post/cuda_vs_rocm.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-474">一、 铁王座：CUDA 凭什么成为英伟达的护城河？</a></li>
<li><a href="#toc-82b">二、 只是致敬，并非抄袭：ROCm 与 CUDA 的底层异同</a></li>
<li><a href="#toc-959">1. 硬件控制原理：高度一致</a></li>
<li><a href="#toc-c80">2. 软件生态构建：闭源黑盒 vs. 开源标准</a></li>
<li><a href="#toc-da2">三、 悖论：AI 正在摧毁毁 CUDA 的围墙？</a></li>
<li><a href="#toc-47d">1. 自动重写：生成式 AI 抹平了代码迁移成本</a></li>
<li><a href="#toc-7ec">2. 降维打击：大一统中间件（Triton）的崛起</a></li>
<li><a href="#toc-143">四、 反击：英伟达的防御与应对措施</a></li>
<li><a href="#toc-f38">1. 从“卖芯片”彻底转变为“卖数据中心系统”</a></li>
<li><a href="#toc-1ee">2. 恐怖的“摩尔定律”速度压制（时间窗口战）</a></li>
<li><a href="#toc-dd3">五、 展望：双雄逐鹿的终局走向</a></li>
<li><a href="#toc-f4e">英伟达（NVIDIA）：向系统级平台与云计算巨头演进</a></li>
<li><a href="#toc-382">AMD：在推理（Inference）与企业级市场迎来历史性爆发</a></li>
<li><a href="#toc-ebb">总结：AI 没有毁灭英伟达，但 AI 会解放硬件市场</a></li>
</ul>
</div><p>在硅谷的商业史上，很少有一家企业能像英伟达（NVIDIA）这样，凭借一套软件生态筑起数千亿美元的商业高墙。这堵墙的名字叫 CUDA。</p>
<p>长期以来，业界形成了一个牢不可破的共识：买英伟达是为了它的硬件，而留下来是因为它的软件。 </p>
<p>然而，历史往往充满了反讽。随着由英伟达亲手引爆的生成式 AI 浪潮走向纵深，一个意想不到的“回旋镖”正加速飞回：AI 越强大，重写和迁移 CUDA 代码的门槛就越低。英伟达引领的 AI 革命，正在反向削弱它自己最引以为傲的软件护城河。</p>
<p>这是一场关于编译器、中间件、开源力量与人工智能自我进化的硬核博弈。</p>
<hr>
<h2><a id="toc-474" class="anchor" href="#toc-474"></a>一、 铁王座：CUDA 凭什么成为英伟达的护城河？</h2>
<p>要理解城墙是如何倒塌的，首先要明白它是如何建立的。</p>
<p>在 2006 年之前，GPU（图形处理器）只是单纯的游戏显卡。如果科学家想要用 GPU 运行数学矩阵运算，必须把数学公式伪装成“图形渲染指令”喂给显卡，编写过程极其痛苦。</p>
<p>2006 年，英伟达推出了 CUDA（统一计算设备架构）。它的核心贡献在于：允许程序员直接使用 C/C++ 语言来编写控制 GPU 的并行计算代码。</p>
<p><img src="https://thinkinpython.com/static/upload/20260605/upload_ce195777da0904e824007b90be24afde.png" alt="image.png"></p>
<p>为了推广 CUDA，黄仁勋做出了一个在当时看来极其疯狂且亏损巨大的决定：强制让英伟达出厂的所有显卡（包括千元级的GeForce游戏显卡）都必须内置 CUDA 模块。事实证明老黄的这一决策极具远见！</p>
<p>这一决定为英伟达创造了一个价值数千亿的护城河，带来了两个决定性的商业结果：</p>
<ol>
<li>人才基础绝对垄断：过去 18 年里，全球无数的高校学生、科研人员和独立开发者，只需用自己的游戏电脑就能零门槛学习 CUDA。当这批人毕业进入大模型公司或云巨头企业时，他们只会使用 CUDA。</li>
<li>Day-0 生态锁死：全球几乎所有的 AI 开源论文、创新大模型（如 Transformer、Diffusion、Sora），在 GitHub 上发布的第一天，默认代码全部基于 CUDA 编写。</li>
</ol>
<p>英伟达借此收起了超过 75% 的高昂硬件毛利，史称 “英伟达税”。企业想要更便宜的硬件？对不起，你离不开 CUDA。</p>
<p>天下苦英伟达久矣，苍天已死，ROCm当立！</p>
<hr>
<h2><a id="toc-82b" class="anchor" href="#toc-82b"></a>二、 只是致敬，并非抄袭：ROCm 与 CUDA 的底层异同</h2>
<p>为了打破英伟达的垄断，AMD 在 2016 年推出了开源的 ROCm（Radeon Open Compute platform）。从技术实现原理来看，两者的底层逻辑呈现出“异曲同工”，但在软件栈的构建思路上却“背道而驰”。</p>
<h2><a id="toc-959" class="anchor" href="#toc-959"></a>1. 硬件控制原理：高度一致</h2>
<p>在最底层的芯片控制上，CUDA 和 ROCm 都基于 SIMT（单指令多线程） 架构。两者的核心概念在物理硬件上几乎是一一对应的：</p>
<ul>
<li>英伟达的 Thread≈ AMD 的 Work-item</li>
<li>英伟达的 Warp（32线程）≈ AMD 的 Wavefront（波前，32/64线程）</li>
<li>英伟达的 Block（线程块）≈ AMD 的 Work-group</li>
</ul>
<p>因此，无论是 CUDA 还是 ROCm，优化矩阵运算和控制显存缓存的数学逻辑在本质上是互通的。</p>
<h2><a id="toc-c80" class="anchor" href="#toc-c80"></a>2. 软件生态构建：闭源黑盒 vs. 开源标准</h2>
<p>两者的真正差异在于编译器和代码的生成机制：</p>
<ul>
<li><p>CUDA 的 NVCC 编译器：英伟达采用全自研且闭源的 NVCC。它将代码编译成一种私有的虚拟中间语言 PTX，再通过闭源驱动实时翻译成特定显卡的机器码。其内部的数学加速库（如 cuDNN、TensorRT）经过了 18 年的黑盒调优，外界无法窥探。</p>
</li>
<li><p>ROCm 的 LLVM 生态（开源公路）：AMD 没有从头自研编译器，而是直接拥抱了工业标准的开源编译器框架 LLVM。AMD 开发了 HIP（可移植异构接口） 技术，作为代码的桥接层。</p>
</li>
</ul>
<p><img src="https://thinkinpython.com/static/upload/20260605/upload_4ff1569c776f090312add07088421227.png" alt="hip.png"></p>
<p>AMD 的战略很明确：通过 HIP 提供一个“一键翻译工具（hipify）”，试图让开发者把现有的 CUDA 代码自动翻译成 HIP 代码，从而实现“一次编写，到处运行”。</p>
<p>然而，在过去几年中，ROCm 的市场份额依然极低。其原因不在于硬件参数，而在于迁移成本的不可承受之重。早期的 ROCm 充满了编译 Bug、文档缺失、且由于缺乏类似英伟达的群众基础，企业为了将 CUDA 迁移到 ROCm，需要雇佣极其昂贵的系统级工程师进行手动调优，排查诡异的编译器错误。在分秒必争的 AI 竞赛中，没有公司愿意承担这种时间成本。</p>
<p>直到生成式 AI 的爆发，彻底改变了博弈的底层规则。</p>
<hr>
<h2><a id="toc-da2" class="anchor" href="#toc-da2"></a>三、 悖论：AI 正在摧毁毁 CUDA 的围墙？</h2>
<p>英伟达引以为傲的 AI 技术，正在成为其软件护城河最致命的“特洛伊木马”。这种蚕食主要通过两个路径发生：</p>
<h2><a id="toc-47d" class="anchor" href="#toc-47d"></a>1. 自动重写：生成式 AI 抹平了代码迁移成本</h2>
<p>过去需要一个顶级专家团队耗时数月才能完成的“CUDA 到 ROCm/HIP”的代码重写与调优工作，现在正在被大语言模型（如 Claude 3.5、GPT-4o）以分钟级的时间彻底抹平。</p>
<p>现代 AI 编程大模型对底层的抽象语法树（AST）和 GPU 显存对齐有着完美的理解。AI 能够轻松识别 CUDA 代码中的专有算子，不仅能进行语法替换，还能根据 AMD 的硬件特性自动重写出高度优化、无 Bug 的 HIP 算子。</p>
<p>这构成了科技史上最讽刺的商业闭环：</p>
<p>英伟达售卖昂贵芯片 ➔ 科技巨头购买并训练出强大的生成式 AI ➔ 巨头用这个 AI 自动将 CUDA 代码重写迁移为 ROCm ➔ 巨头大规模转向采购便宜的 AMD 芯片 ➔ 摆脱英伟达</p>
<h2><a id="toc-7ec" class="anchor" href="#toc-7ec"></a>2. 降维打击：大一统中间件（Triton）的崛起</h2>
<p>比 AI 自动写代码更致命的，是 AI 底层基础设施本身的架构演进——以 OpenAI 主导的 Triton 语言为代表的中间件迅速崛起。</p>
<p>在过去，深度学习框架（如 PyTorch）的底层需要针对英伟达编写大量的 CUDA 算子。而 OpenAI 发布的 Triton，是一种极简的、基于 Python 的开源编译器。它的目标是让普通程序员写出 Python 级别的简易代码，由 Triton 编译器自动去处理底层的并行和内存管理。</p>
<p>最关键的是：Triton 在设计之初，就同时开发了 NVIDIA 和 AMD 的双后端。</p>
<p><img src="https://thinkinpython.com/static/upload/20260605/upload_db43c2a4845dd5640c0727f0f2a7374b.png" alt="image.png"></p>
<p>当 OpenAI、Meta 等大模型厂商逐渐将自家的核心模型从直接调用 CUDA 转向通过 Triton 编写时，底层的硬件开始变得完全透明和可替换。英伟达精心构筑的 CUDA 软件墙，正在被 Triton 这种中间件从内部逐步“瓦解”。</p>
<hr>
<h2><a id="toc-143" class="anchor" href="#toc-143"></a>四、 反击：英伟达的防御与应对措施</h2>
<p>面对软件护城河被 AI 和开源生态反向侵蚀的危机，英伟达的竞争策略已经发生了重大位移，他们正在将防御阵线从“单卡软件”推向更难被攻破的<strong>物理极限</strong>。</p>
<h2><a id="toc-f38" class="anchor" href="#toc-f38"></a>1. 从“卖芯片”彻底转变为“卖数据中心系统”</h2>
<p>当单卡的软件壁垒逐渐被抹平时，英伟达开始在超大规模集群的网络互联上加高壁垒。</p>
<ul>
<li><p>大模型训练现在已经进入万卡、十万卡时代，芯片之间的通信延迟比单卡算力更重要。</p>
</li>
<li><p>英伟达通过私有的 NVLink 协议、NVSwitch 芯片以及收购 Mellanox 获得的 InfiniBand（IB）网络技术，将上万张显卡织成一整个极其高效的“超级大脑”。这种万卡级别的物理网络拓扑、高频通信软硬件的极限整合，是单靠大模型“重写几行代码”绝对无法跨越的物理硬实力。</p>
</li>
</ul>
<h2><a id="toc-1ee" class="anchor" href="#toc-1ee"></a>2. 恐怖的“摩尔定律”速度压制（时间窗口战）</h2>
<p>英伟达正在采用商业史上罕见的高强度研发节奏，将硬件迭代速度提升至一年一代（从 Hopper 到 Blackwell，再到 Rubin 架构）。</p>
<p>即使 AI 能完美、零成本地将 CUDA 代码迁移到 AMD 的硬件上，如果英伟达新一代硬件的绝对性能依然能拉开对手一代以上的差距，那么理性的企业为了抢夺模型上线的关键时间窗口（Time-to-Market），依然不得不乖乖向英伟达奉上高额的溢价。</p>
<hr>
<h2><a id="toc-dd3" class="anchor" href="#toc-dd3"></a>五、 展望：双雄逐鹿的终局走向</h2>
<p>随着软件壁垒的消融，AI 芯片竞争的下半场，正在从过去的“生态垄断战”逐步回归到最纯粹的“硬件性价比、功耗比以及大规模网络互联能力”的物理对决。</p>
<h2><a id="toc-f4e" class="anchor" href="#toc-f4e"></a>英伟达（NVIDIA）：向系统级平台与云计算巨头演进</h2>
<p>英伟达的短期地位依然难以撼动。虽然单卡 CUDA 的壁垒在降低，但它凭借全栈的数据中心网络（NVLink）和一年一代的恐怖迭代速度，依然会牢牢占据最顶尖、最追求极致性能的超大规模 AI 训练市场（AI Training）。英伟达未来的角色将更像是一个“AI 基础设施的超级总承包商”。</p>
<h2><a id="toc-382" class="anchor" href="#toc-382"></a>AMD：在推理（Inference）与企业级市场迎来历史性爆发</h2>
<p>对于 AMD 而言，这是历史上最好的红利期。随着全球大模型逐渐从“训练阶段”走向“大规模商业落地推理阶段”，市场对算力的需求正在从“不计成本追求极限性能”转向“追求极致的每美元性价比和功耗比”。</p>
<p>在 PyTorch、Triton 的加持以及 AI 自动迁移工具的普及下，ROCm 的软件劣势正在被快速拉平。AMD 的 Instinct 系列芯片凭借更大的显存容量和高性价比，将极大程度地蚕食云巨头、传统企业级私有化部署的推理算力市场。</p>
<h2><a id="toc-ebb" class="anchor" href="#toc-ebb"></a>总结：AI 没有毁灭英伟达，但 AI 会解放硬件市场</h2>
<p>这场由英伟达亲手点燃的 AI 圣火，在不久的将来，会解除在其他硬件厂商身上的 CUDA 枷锁，让整个芯片行业重新回到了以物理性能与创新效率为核心的良性竞争赛道上。这算不算一个美好的愿望？！（Doggy）</p>

            ]]></description>
            <pubDate>Wed, 03 Jun 2026 13:58:41 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/cuda_vs_rocm.html</guid>
        </item>
        <item>
            <title>在线加密工具</title>
            <link>http://www.thinkinpython.com/post/rsa_encryt.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#%E5%9C%A8%E7%BA%BF%E5%8A%A0%E5%AF%86%E5%B7%A5%E5%85%B7-staticencrypthtml">在线加密工具 <a href="/static/encrypt.html"></a></a><ul>
<li><a href="#howto">How to</a></li>
</ul>
</li>
<li><a href="#toc-f0d">1. 接收方准备公钥</a></li>
<li><a href="#toc-194">发送方加密信息</a></li>
<li><a href="#toc-1d8">接收方解密信息</a></li>
</ul>
</div><h2><a id="toc-a56" class="anchor" href="#toc-a56"></a>在线加密工具 <a href="/static/encrypt.html">&lt;入口&gt;</a></h2>
<h1><a id="howto" class="anchor" href="#howto"></a>How to</h1>
<h2><a id="toc-f0d" class="anchor" href="#toc-f0d"></a>1. 接收方准备公钥</h2>
<p><img src="https://thinkinpython.com/static/upload/20260521/upload_722130fdcd2894cb695252372ca3a9c7.png" alt="image.png"></p>
<h2><a id="toc-194" class="anchor" href="#toc-194"></a>发送方加密信息</h2>
<p>只要发送方没有更换公钥，该公钥可以一直使用。只需更新明文信息，点击加密按钮即可生成新的秘文。</p>
<p><img src="https://thinkinpython.com/static/upload/20260521/upload_e9bd9a4d558182347b3a77c871a9fccb.png" alt="image.png"></p>
<h2><a id="toc-1d8" class="anchor" href="#toc-1d8"></a>接收方解密信息</h2>
<p>只要不刷新页面，可以持续解密信息。</p>
<p><img src="https://thinkinpython.com/static/upload/20260521/upload_7d6c47f271c1e75ae80e30554e84c31b.png" alt="image.png"></p>

            ]]></description>
            <pubDate>Sun, 10 May 2026 18:22:56 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/rsa_encryt.html</guid>
        </item>
        <item>
            <title>大型代码库的 Claude Code 最佳实践：构建层级化知识库</title>
            <link>http://www.thinkinpython.com/post/claude-code-large-codebase-best-practices-cn.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-dd2">大型代码库的 Claude Code 最佳实践：构建层级化知识库</a><ul>
<li><a href="#toc-e45">引言</a></li>
<li><a href="#toc-903">核心原则：渐进式披露</a></li>
<li><a href="#toc-9dc">三级层级结构</a><ul>
<li><a href="#toc-887">第一层：项目根目录（全局上下文）</a></li>
<li><a href="#toc-b86">第二层：模块/领域（战略上下文）</a></li>
<li><a href="#toc-4a7">第三层：叶子节点/组件（战术上下文）</a></li>
</ul>
</li>
<li><a href="#toc-1d3">AI 友好内容的编写规范</a><ul>
<li><a href="#toc-81d">1. 明确而非习惯用语</a></li>
<li><a href="#toc-157">2. 使用&quot;要/不要&quot;列表</a></li>
<li><a href="#toc-9b6">3. 引用具体文件路径</a></li>
<li><a href="#toc-dd2">4. 保持精简</a></li>
</ul>
</li>
<li><a href="#toc-794">层级化 vs 扁平化：对比</a></li>
<li><a href="#toc-0b2">实施步骤</a><ul>
<li><a href="#toc-981">第一阶段：根目录</a></li>
<li><a href="#toc-862">第二阶段：核心模块</a></li>
<li><a href="#toc-a6d">第三阶段：复杂组件</a></li>
<li><a href="#toc-6ad">第四阶段：让 Claude 协助</a></li>
</ul>
</li>
<li><a href="#toc-25f">总结</a></li>
</ul>
</li>
</ul>
</div><h1><a id="toc-dd2" class="anchor" href="#toc-dd2"></a>大型代码库的 Claude Code 最佳实践：构建层级化知识库</h1>
<blockquote>
<p><strong>摘要</strong>：在大型代码库中高效使用 Claude Code 的核心在于&quot;渐进式披露&quot;原则。通过构建三级层级化知识库，让 AI 在需要时获取恰到好处的上下文，避免 token 浪费和上下文混淆。</p>
</blockquote>
<hr>
<p><a href="/post/claude-code-large-codebase-best-practices.html">to English</a></p>
<h2><a id="toc-e45" class="anchor" href="#toc-e45"></a>引言</h2>
<p>AI 辅助编程工具日益普及，开发者面临新挑战：如何在大型代码库中高效使用 Claude Code。在根目录放一个庞大的 README 并非良策——token 浪费、上下文混淆、维护困难。</p>
<p>本文介绍一种<strong>层级化知识库</strong>最佳实践，通过三级文档结构，让 Claude Code 在正确时机获取正确信息。</p>
<hr>
<h2><a id="toc-903" class="anchor" href="#toc-903"></a>核心原则：渐进式披露</h2>
<p><strong>渐进式披露（Progressive Disclosure）</strong>：在根目录提供高层规则，随着 AI 深入子目录，逐步提供更具体的&quot;为什么&quot;和&quot;怎么做&quot;。</p>
<p>Claude Code 原生识别 <code>CLAUDE.md</code> 文件。我们将此模式扩展为层级化方法，让文档结构与目录树对齐。</p>
<hr>
<h2><a id="toc-9dc" class="anchor" href="#toc-9dc"></a>三级层级结构</h2>
<h3><a id="toc-887" class="anchor" href="#toc-887"></a>第一层：项目根目录（全局上下文）</h3>
<p><strong>文件位置</strong>：<code>/CLAUDE.md</code></p>
<p><strong>定位</strong>：项目&quot;宪法&quot;，定义技术栈、核心命令、不可违背的标准。</p>
<p><strong>示例内容</strong>：</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## 构建命令</span>
<span class="hljs-bullet">- </span>生产构建：pnpm build
<span class="hljs-bullet">- </span>开发模式：pnpm dev

<span class="hljs-section">## 测试命令</span>
<span class="hljs-bullet">- </span>单元测试：pnpm test:unit <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">文件</span>&gt;</span></span>
<span class="hljs-bullet">- </span>E2E 测试：pnpm test:e2e

<span class="hljs-section">## 代码规范</span>
<span class="hljs-bullet">- </span>仅使用函数式组件
<span class="hljs-bullet">- </span>禁止 default exports
<span class="hljs-bullet">- </span>样式使用 Tailwind CSS

<span class="hljs-section">## 架构概述</span>
<span class="hljs-bullet">- </span>Next.js 单体仓库
<span class="hljs-bullet">- </span>共享逻辑位于 /packages/shared
</code></pre>
<hr>
<h3><a id="toc-b86" class="anchor" href="#toc-b86"></a>第二层：模块/领域（战略上下文）</h3>
<p><strong>文件位置</strong>：<code>/src/features/billing/CONTEXT.md</code></p>
<p><strong>定位</strong>：解释代码无法揭示的业务逻辑和隐性数据流。</p>
<p><strong>示例内容</strong>：</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## 领域逻辑</span>
本模块处理 Stripe 支付集成。

<span class="hljs-section">## 数据流</span>
所有支付必须先触发 webhook-handler，再更新数据库。

<span class="hljs-section">## 安全要求</span>
<span class="hljs-bullet">- </span>禁止在前端暴露 Secret_Key
<span class="hljs-bullet">- </span>使用 /api/stripe 中的代理进行后端调用

<span class="hljs-section">## 依赖关系</span>
<span class="hljs-bullet">- </span>依赖 UserStore 进行税费计算
<span class="hljs-bullet">- </span>与 OrderService 双向同步
</code></pre>
<hr>
<h3><a id="toc-4a7" class="anchor" href="#toc-4a7"></a>第三层：叶子节点/组件（战术上下文）</h3>
<p><strong>文件位置</strong>：<code>/src/components/DataGrid/NOTES.md</code></p>
<p><strong>定位</strong>：解释&quot;陷阱&quot;和技术债务。</p>
<p><strong>示例内容</strong>：</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## 性能注意事项</span>
<span class="hljs-bullet">- </span>使用 react-virtualized 进行虚拟滚动
<span class="hljs-bullet">- </span>禁止移除 rowHeight 属性，否则在 Safari 上会崩溃

<span class="hljs-section">## 已知问题</span>
<span class="hljs-bullet">- </span>排序切换与 API 存在竞态条件
<span class="hljs-bullet">- </span>解决方案：使用 isLoading ref 进行防抖

<span class="hljs-section">## 待重构</span>
<span class="hljs-bullet">- </span>[ ] 将排序逻辑提取为独立 Hook
<span class="hljs-bullet">- </span>[ ] 替换已废弃的 componentWillReceiveProps
</code></pre>
<hr>
<h2><a id="toc-1d3" class="anchor" href="#toc-1d3"></a>AI 友好内容的编写规范</h2>
<h3><a id="toc-81d" class="anchor" href="#toc-81d"></a>1. 明确而非习惯用语</h3>
<p>❌ 错误：<code>Just run the tests</code><br>✅ 正确：<code>Use npm run test</code></p>
<p>AI 对明确指令响应更好，避免开发者&quot;行话&quot;。</p>
<h3><a id="toc-157" class="anchor" href="#toc-157"></a>2. 使用&quot;要/不要&quot;列表</h3>
<p>AI 对否定约束响应极佳：</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## 类型规范</span>
<span class="hljs-bullet">- </span>❌ 不要使用 'any' 类型
<span class="hljs-bullet">- </span>✅ 类型 truly 不明确时使用 'unknown'
<span class="hljs-bullet">- </span>✅ 优先使用 TypeScript 严格模式
</code></pre>
<h3><a id="toc-9b6" class="anchor" href="#toc-9b6"></a>3. 引用具体文件路径</h3>
<p>让 AI 知道&quot;真相&quot;在哪里：</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## 数据模型</span>
<span class="hljs-bullet">- </span>主 Schema 定义在 /src/db/schema.ts
<span class="hljs-bullet">- </span>类型导出在 /src/types/index.ts
</code></pre>
<h3><a id="toc-dd2" class="anchor" href="#toc-dd2"></a>4. 保持精简</h3>
<p>单个上下文文件<strong>不超过 50 行</strong>高密度信息。超过 10KB，AI 可能浪费过多 token。</p>
<hr>
<h2><a id="toc-794" class="anchor" href="#toc-794"></a>层级化 vs 扁平化：对比</h2>
<table>
<thead>
<tr>
<th>特性</th>
<th>扁平化（单一 README）</th>
<th>层级化（多级结构）</th>
</tr>
</thead>
<tbody>
<tr>
<td>Token 使用</td>
<td>高（AI 每次读取全部内容）</td>
<td>低（仅读取相关目录笔记）</td>
</tr>
<tr>
<td>精确度</td>
<td>低（可能混淆 Webhook 规则与 UI 规则）</td>
<td>高（特定文件夹的特定规则）</td>
</tr>
<tr>
<td>可维护性</td>
<td>难（单一文件变成&quot;杂物抽屉&quot;）</td>
<td>易（小文件贴近所描述的代码）</td>
</tr>
<tr>
<td>扩展性</td>
<td>差（随项目增长迅速膨胀）</td>
<td>好（新增模块只需添加对应文件）</td>
</tr>
</tbody>
</table>
<hr>
<h2><a id="toc-0b2" class="anchor" href="#toc-0b2"></a>实施步骤</h2>
<p>无需一次性完成。采用渐进式方法：</p>
<h3><a id="toc-981" class="anchor" href="#toc-981"></a>第一阶段：根目录</h3>
<ol>
<li>创建 <code>/CLAUDE.md</code></li>
<li>定义技术栈、构建命令、代码规范</li>
</ol>
<h3><a id="toc-862" class="anchor" href="#toc-862"></a>第二阶段：核心模块</h3>
<ol>
<li>识别 3-5 个核心业务模块</li>
<li>为每个模块创建 <code>CONTEXT.md</code></li>
<li>描述数据流、安全要求、依赖关系</li>
</ol>
<h3><a id="toc-a6d" class="anchor" href="#toc-a6d"></a>第三阶段：复杂组件</h3>
<ol>
<li>识别存在技术债务或&quot;陷阱&quot;的组件</li>
<li>创建 <code>NOTES.md</code> 记录已知问题和解决方案</li>
</ol>
<h3><a id="toc-6ad" class="anchor" href="#toc-6ad"></a>第四阶段：让 Claude 协助</h3>
<p>使用 Claude Code 本身帮助生成文档：</p>
<pre><code class="hljs lang-bash"><span class="hljs-comment"># 让 Claude 分析模块并生成 CONTEXT.md</span>
claude <span class="hljs-string">"Analyze the billing module and create a CONTEXT.md 
        that explains the data flow and security requirements"</span>
</code></pre>
<hr>
<h2><a id="toc-25f" class="anchor" href="#toc-25f"></a>总结</h2>
<p>层级化知识库的核心价值：</p>
<ol>
<li><strong>节省 token</strong>：AI 仅读取相关上下文</li>
<li><strong>提高精确度</strong>：特定规则作用于特定范围</li>
<li><strong>易于维护</strong>：小文件贴近代码，更新成本低</li>
<li><strong>可扩展</strong>：随项目增长自然扩展</li>
</ol>
<p>实施时从根目录 <code>/CLAUDE.md</code> 入手，逐步向下扩展。记住：<strong>文档的价值在于被使用，而非被写完</strong>。</p>
<hr>
<p><strong>参考资料</strong></p>
<ul>
<li><a href="https://docs.anthropic.com/claude-code">Claude Code 官方文档</a></li>
<li><a href="https://www.nngroup.com/articles/progressive-disclosure/">Progressive Disclosure in Software Design</a></li>
</ul>

            ]]></description>
            <pubDate>Tue, 31 Mar 2026 04:36:26 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/claude-code-large-codebase-best-practices-cn.html</guid>
        </item>
        <item>
            <title>Best Practices for Large Codebases with Claude Code: Building a Level-Structured Knowledge Base</title>
            <link>http://www.thinkinpython.com/post/claude-code-large-codebase-best-practices.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-699">Best Practices for Large Codebases with Claude Code: Building a Level-Structured Knowledge Base</a><ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#toc-562">Core Principle: Progressive Disclosure</a></li>
<li><a href="#the3-tierhierarchy">The 3-Tier Hierarchy</a><ul>
<li><a href="#toc-05b">Tier 1: Project Root (Global Context)</a></li>
<li><a href="#toc-aaa">Tier 2: Module/Domain (Strategic Context)</a></li>
<li><a href="#toc-822">Tier 3: Leaf/Component (Tactical Context)</a></li>
</ul>
</li>
<li><a href="#toc-6ea">Best Practices for &quot;AI-Friendly&quot; Content</a><ul>
<li><a href="#toc-267">1. Be Explicit, Not Idiomatic</a></li>
<li><a href="#toc-c43">2. Use &quot;Do/Don&#39;t&quot; Lists</a></li>
<li><a href="#toc-5f4">3. Reference Specific Files</a></li>
<li><a href="#toc-eb3">4. Keep It Small</a></li>
</ul>
</li>
<li><a href="#toc-dca">Hierarchical vs. Flat: Comparison</a></li>
<li><a href="#implementationsteps">Implementation Steps</a><ul>
<li><a href="#toc-c7a">Phase 1: Root Directory</a></li>
<li><a href="#toc-959">Phase 2: Core Modules</a></li>
<li><a href="#toc-13e">Phase 3: Complex Components</a></li>
<li><a href="#toc-404">Phase 4: Let Claude Help</a></li>
</ul>
</li>
<li><a href="#summary">Summary</a></li>
</ul>
</li>
</ul>
</div><h1><a id="toc-699" class="anchor" href="#toc-699"></a>Best Practices for Large Codebases with Claude Code: Building a Level-Structured Knowledge Base</h1>
<blockquote>
<p><strong>Abstract</strong>: The key to efficiently using Claude Code in large codebases lies in the principle of &quot;Progressive Disclosure.&quot; By building a three-tier hierarchical knowledge base, AI can access just the right amount of context when needed, avoiding token waste and context confusion.</p>
</blockquote>
<hr>
<p><a href="/post/claude-code-large-codebase-best-practices-cn.html">中文版</a></p>
<h2><a id="introduction" class="anchor" href="#introduction"></a>Introduction</h2>
<p>As AI-assisted programming tools become ubiquitous, developers face a new challenge: how to use Claude Code effectively in large codebases. Placing a massive README file at the root is far from optimal—it leads to token waste, context confusion, and maintenance difficulties.</p>
<p>This article presents a <strong>hierarchical knowledge base</strong> best practice, using a three-tier documentation structure to ensure Claude Code receives the right information at the right time.</p>
<hr>
<h2><a id="toc-562" class="anchor" href="#toc-562"></a>Core Principle: Progressive Disclosure</h2>
<p><strong>Progressive Disclosure</strong> means providing high-level rules at the root, then offering increasingly specific &quot;why&quot; and &quot;how&quot; details as AI drills down into subdirectories.</p>
<p>Claude Code natively recognizes <code>CLAUDE.md</code> files. We can extend this pattern into a hierarchical approach, aligning documentation structure with the directory tree.</p>
<hr>
<h2><a id="the3-tierhierarchy" class="anchor" href="#the3-tierhierarchy"></a>The 3-Tier Hierarchy</h2>
<h3><a id="toc-05b" class="anchor" href="#toc-05b"></a>Tier 1: Project Root (Global Context)</h3>
<p><strong>File Location</strong>: <code>/CLAUDE.md</code></p>
<p><strong>Purpose</strong>: The &quot;Constitution&quot;—defining the tech stack, core commands, and non-negotiable standards.</p>
<p><strong>Example Content</strong>:</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## Build Commands</span>
<span class="hljs-bullet">- </span>Production build: <span class="hljs-code">`pnpm build`</span>
<span class="hljs-bullet">- </span>Development mode: <span class="hljs-code">`pnpm dev`</span>

<span class="hljs-section">## Test Commands</span>
<span class="hljs-bullet">- </span>Unit tests: <span class="hljs-code">`pnpm test:unit &lt;file&gt;`</span>
<span class="hljs-bullet">- </span>E2E tests: <span class="hljs-code">`pnpm test:e2e`</span>

<span class="hljs-section">## Code Style</span>
<span class="hljs-bullet">- </span>Functional components only
<span class="hljs-bullet">- </span>No default exports
<span class="hljs-bullet">- </span>Use Tailwind CSS for styling

<span class="hljs-section">## Architecture</span>
<span class="hljs-bullet">- </span>Next.js monorepo
<span class="hljs-bullet">- </span>Shared logic in <span class="hljs-code">`/packages/shared`</span>
</code></pre>
<hr>
<h3><a id="toc-aaa" class="anchor" href="#toc-aaa"></a>Tier 2: Module/Domain (Strategic Context)</h3>
<p><strong>File Location</strong>: <code>/src/features/billing/CONTEXT.md</code></p>
<p><strong>Purpose</strong>: Explaining business logic and invisible data flows that code alone won&#39;t reveal.</p>
<p><strong>Example Content</strong>:</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## Domain Logic</span>
This module handles Stripe integration.

<span class="hljs-section">## Data Flow</span>
All payments must trigger the <span class="hljs-code">`webhook-handler`</span> before updating the DB.

<span class="hljs-section">## Security</span>
<span class="hljs-bullet">- </span>Never expose Secret_Key to the frontend
<span class="hljs-bullet">- </span>Use the proxy in <span class="hljs-code">`/api/stripe`</span> for backend calls

<span class="hljs-section">## Dependencies</span>
<span class="hljs-bullet">- </span>Depends on UserStore for tax calculations
<span class="hljs-bullet">- </span>Bidirectional sync with OrderService
</code></pre>
<hr>
<h3><a id="toc-822" class="anchor" href="#toc-822"></a>Tier 3: Leaf/Component (Tactical Context)</h3>
<p><strong>File Location</strong>: <code>/src/components/DataGrid/NOTES.md</code></p>
<p><strong>Purpose</strong>: Explaining &quot;gotchas&quot; and specific technical debt.</p>
<p><strong>Example Content</strong>:</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## Performance</span>
<span class="hljs-bullet">- </span>Uses react-virtualized for virtual scrolling
<span class="hljs-bullet">- </span>Do not remove the <span class="hljs-code">`rowHeight`</span> prop or it will crash on Safari

<span class="hljs-section">## Known Issues</span>
<span class="hljs-bullet">- </span>Sorting toggle has a race condition with the API
<span class="hljs-bullet">- </span>Workaround: use <span class="hljs-code">`isLoading`</span> ref to debounce

<span class="hljs-section">## TODO</span>
<span class="hljs-bullet">- </span>[ ] Extract sorting logic into a standalone Hook
<span class="hljs-bullet">- </span>[ ] Replace deprecated componentWillReceiveProps
</code></pre>
<hr>
<h2><a id="toc-6ea" class="anchor" href="#toc-6ea"></a>Best Practices for &quot;AI-Friendly&quot; Content</h2>
<h3><a id="toc-267" class="anchor" href="#toc-267"></a>1. Be Explicit, Not Idiomatic</h3>
<p>❌ Wrong: <code>Just run the tests</code><br>✅ Right: <code>Use npm run test</code></p>
<p>AI models respond better to explicit instructions. Avoid developer &quot;jargon.&quot;</p>
<h3><a id="toc-c43" class="anchor" href="#toc-c43"></a>2. Use &quot;Do/Don&#39;t&quot; Lists</h3>
<p>AI models respond exceptionally well to negative constraints:</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## Type Safety</span>
<span class="hljs-bullet">- </span>❌ Don't use 'any' types
<span class="hljs-bullet">- </span>✅ Use 'unknown' if the type is truly ambiguous
<span class="hljs-bullet">- </span>✅ Prefer TypeScript strict mode
</code></pre>
<h3><a id="toc-5f4" class="anchor" href="#toc-5f4"></a>3. Reference Specific Files</h3>
<p>Let AI know where &quot;The Truth&quot; lives:</p>
<pre><code class="hljs lang-markdown"><span class="hljs-section">## Data Models</span>
<span class="hljs-bullet">- </span>Master schema defined in <span class="hljs-code">`/src/db/schema.ts`</span>
<span class="hljs-bullet">- </span>Type exports in <span class="hljs-code">`/src/types/index.ts`</span>
</code></pre>
<h3><a id="toc-eb3" class="anchor" href="#toc-eb3"></a>4. Keep It Small</h3>
<p>Individual context files should be <strong>under 50 lines</strong> of high-density information. If a file exceeds 10KB, AI may waste too many tokens reading it.</p>
<hr>
<h2><a id="toc-dca" class="anchor" href="#toc-dca"></a>Hierarchical vs. Flat: Comparison</h2>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Flat (One Big README)</th>
<th>Hierarchical (Level-Structured)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Token Usage</td>
<td>High (Claude reads everything every time)</td>
<td>Low (Claude only reads relevant folder&#39;s notes)</td>
</tr>
<tr>
<td>Precision</td>
<td>Low (May confuse Webhook rules with UI rules)</td>
<td>High (Specific rules for specific folders)</td>
</tr>
<tr>
<td>Maintainability</td>
<td>Hard (One file becomes a &quot;junk drawer&quot;)</td>
<td>Easy (Small files stay close to the code they describe)</td>
</tr>
<tr>
<td>Scalability</td>
<td>Poor (Rapidly bloats as project grows)</td>
<td>Good (New modules just add corresponding files)</td>
</tr>
</tbody>
</table>
<hr>
<h2><a id="implementationsteps" class="anchor" href="#implementationsteps"></a>Implementation Steps</h2>
<p>You don&#39;t have to write all of this at once. Adopt a progressive approach:</p>
<h3><a id="toc-c7a" class="anchor" href="#toc-c7a"></a>Phase 1: Root Directory</h3>
<ol>
<li>Create <code>/CLAUDE.md</code></li>
<li>Define tech stack, build commands, code conventions</li>
</ol>
<h3><a id="toc-959" class="anchor" href="#toc-959"></a>Phase 2: Core Modules</h3>
<ol>
<li>Identify 3-5 core business modules</li>
<li>Create <code>CONTEXT.md</code> for each module</li>
<li>Document data flows, security requirements, dependencies</li>
</ol>
<h3><a id="toc-13e" class="anchor" href="#toc-13e"></a>Phase 3: Complex Components</h3>
<ol>
<li>Identify components with technical debt or &quot;gotchas&quot;</li>
<li>Create <code>NOTES.md</code> documenting known issues and workarounds</li>
</ol>
<h3><a id="toc-404" class="anchor" href="#toc-404"></a>Phase 4: Let Claude Help</h3>
<p>Use Claude Code itself to help generate documentation:</p>
<pre><code class="hljs lang-bash"><span class="hljs-comment"># Ask Claude to analyze a module and generate CONTEXT.md</span>
claude <span class="hljs-string">"Analyze the billing module and create a CONTEXT.md 
        that explains the data flow and security requirements"</span>
</code></pre>
<hr>
<h2><a id="summary" class="anchor" href="#summary"></a>Summary</h2>
<p>The core value of a hierarchical knowledge base:</p>
<ol>
<li><strong>Saves tokens</strong>: AI reads only relevant context</li>
<li><strong>Improves precision</strong>: Specific rules apply to specific scopes</li>
<li><strong>Easy maintenance</strong>: Small files stay close to code, low update cost</li>
<li><strong>Scalable</strong>: Grows naturally with the project</li>
</ol>
<p>When starting, begin with <code>/CLAUDE.md</code> at the root, then expand downward. Remember: <strong>The value of documentation lies in being used, not in being finished.</strong></p>
<hr>
<p><strong>References</strong></p>
<ul>
<li><a href="https://docs.anthropic.com/claude-code">Claude Code Official Documentation</a></li>
<li><a href="https://www.nngroup.com/articles/progressive-disclosure/">Progressive Disclosure in Software Design</a></li>
</ul>

            ]]></description>
            <pubDate>Tue, 31 Mar 2026 02:43:30 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/claude-code-large-codebase-best-practices.html</guid>
        </item>
        <item>
            <title>古都之秋</title>
            <link>http://www.thinkinpython.com/post/fall_of_the_ancient_city.html</link>
            <description><![CDATA[
            <div class="toc"></div><p><img src="https://thinkinpython.com/static/upload/20251102/upload_e6cf72beb7b880c3ec9cead25984501f.png" alt="2.png"></p>
<p><img src="https://thinkinpython.com/static/upload/20251102/upload_d63ab713da10fc48f31030de8a09ddcc.png" alt="4.png"></p>
<p><img src="https://thinkinpython.com/static/upload/20251102/upload_e82a73d7a44f953360441e910aa80fed.png" alt="5.png"></p>
<p><img src="https://thinkinpython.com/static/upload/20251102/upload_a62227a287b0de0c9a6303f6a186779b.png" alt="9.png"></p>

            ]]></description>
            <pubDate>Sun, 02 Nov 2025 14:20:37 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/fall_of_the_ancient_city.html</guid>
        </item>
        <item>
            <title>Amber---下一个 shell 脚本何必是 shell script</title>
            <link>http://www.thinkinpython.com/post/amber_script.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-9ed">先睹为快</a></li>
<li><a href="#toc-24c">执行命令</a></li>
<li><a href="#toc-25f">总结</a></li>
</ul>
</div><p>Linux shell script 是我最喜欢的脚本之一，历史悠久，底蕴深厚，shell script 几乎是内核的一部分，托 POSIX 标准的福，你可以在任何 Linux 系统中使用它。但是编写 shell script 体验并不太好，各种奇怪的语法、没有类型检查、孱弱的数组支持，有时候排查很久的问题，仅仅是错误的使用引号、或者多了一个空格。</p>
<p>我很喜欢造轮子，曾打算写一个高级语言，可以编译成 shell script 执行，即使是一些简单的语法糖，也能很大程度提升 shell script 用户的幸福感。前几天发现一个项目 <a href="https://amber-lang.com">Amber</a> 已经做了这些工作，试用了下还不错，语法类似 Ecmas 或者 js，支持 Text、Num、Bool、Null 和 [] 数组，可以直接编译成 shell script，直接拿到任何 shell 中执行，没有任何移植性困扰。</p>
<h1><a id="toc-9ed" class="anchor" href="#toc-9ed"></a>先睹为快</h1>
<p>参考官网的安装指导，只需要一行命令：</p>
<pre><code class="hljs lang-bash">curl -s <span class="hljs-string">"https://raw.githubusercontent.com/Ph0enixKM/AmberNative/master/setup/install.sh"</span> | bash
</code></pre>
<p>请确保你的系统已安装了 curl、bc， 安装以后就可以使用 <code>amber</code> 命令。 下面是一个简单的例子：</p>
<pre><code class="hljs lang-undefined">let fruits = ["apple", "banana", "grape"]

fun show_opt(fruits) {
    loop index, f in fruits {
        echo "{index}: {f}"
    }
}

show_opt(fruits)
</code></pre>
<p>将上面的代码保存为 <code>test1.ab</code>, 执行它</p>
<pre><code class="hljs lang-bash">~/<span class="hljs-built_in">test</span> $ amber test1.ab
0: apple
1: banana
2: grape
</code></pre>
<p>只要你有任何一种变成经验，很容易明白 amber 的基本语法。</p>
<p>上面的 amber 脚本等效的 shell 如何呢？我们可以将 amber 脚本编译为 shell 脚本。</p>
<pre><code class="hljs lang-undefined">amber test1.ab test1.sh
</code></pre>
<p>相比直接执行 amber 脚本，只需要增加一个参数指定编译输出的脚本文件名即可。<code>test1.sh</code> 看起来这样：</p>
<pre><code class="hljs lang-bash">__AMBER_ARRAY_0=(<span class="hljs-string">"apple"</span> <span class="hljs-string">"banana"</span> <span class="hljs-string">"grape"</span>);
__0_fruits=(<span class="hljs-string">"<span class="hljs-variable">${__AMBER_ARRAY_0[@]}</span>"</span>);
<span class="hljs-keyword">function</span> show_opt__0_v0 {
    <span class="hljs-built_in">local</span> fruits=(<span class="hljs-string">"<span class="hljs-variable">${!1}</span>"</span>)
    index=0;
<span class="hljs-keyword">for</span> f <span class="hljs-keyword">in</span> <span class="hljs-string">"<span class="hljs-variable">${fruits[@]}</span>"</span>
<span class="hljs-keyword">do</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">${index}</span>: <span class="hljs-variable">${f}</span>"</span>
        <span class="hljs-built_in">let</span> index=<span class="hljs-variable">${index}</span>+1
<span class="hljs-keyword">done</span>
};
show_opt__0_v0 __0_fruits[@];
__AMBER_FUN_show_opt0_v0__9=<span class="hljs-variable">${__AMBER_FUN_show_opt0_v0}</span>;
<span class="hljs-built_in">echo</span> <span class="hljs-variable">${__AMBER_FUN_show_opt0_v0__9}</span> &gt; /dev/null 2&gt;&amp;1
</code></pre>
<p>相比之下，amber 脚本真的是相当人性化。</p>
<h1><a id="toc-24c" class="anchor" href="#toc-24c"></a>执行命令</h1>
<p>shell 最强大的能力在于可以方便的调用已安装的命令。amber 也具备这样的能力，而且提供了良好的异常处理能力。</p>
<p>基本语法是 </p>
<pre><code class="hljs lang-undefined">$your cmd$ failed { exception handler }
</code></pre>
<p>两个 $ 之间可以是任何有效的 shell 命令，failed 是 amber 的关键字，表示指令的异常处理。</p>
<p>为了正常使用异常机制，异常所在代码要么位于 main block，要么在函数中。amber 可以指定 main 作为脚本入口。</p>
<pre><code class="hljs lang-bash">main {
    <span class="hljs-built_in">let</span> file_name = <span class="hljs-string">"1.txt"</span>
    <span class="hljs-built_in">let</span> cmd = <span class="hljs-string">"cat"</span>
    <span class="hljs-built_in">let</span> file_content = <span class="hljs-variable">${cmd}</span> {file_name}$ failed {
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"{file_name} not exist"</span>
        fail
    }

    <span class="hljs-built_in">echo</span> file_content
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"done"</span>
}
</code></pre><p>这段代码的尝试获取 <code>1.txt</code> 的内容，如果这个文件不存在就报错退出，否则打印文件内容。</p>
<p>指令可以是任何字符串，比如例子中的命令由 <code>cmd</code> 和 <code>file_name</code> 变量拼接生成。<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="4.263ex" height="2.176ex" style="vertical-align: -0.338ex;" viewBox="0 -791.3 1835.5 936.9" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" aria-labelledby="MathJax-SVG-1-Title">
<title id="MathJax-SVG-1-Title">cmd</title>
<defs aria-hidden="true">
<path stroke-width="1" id="E1-MJMATHI-63" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path>
<path stroke-width="1" id="E1-MJMATHI-6D" d="M21 287Q22 293 24 303T36 341T56 388T88 425T132 442T175 435T205 417T221 395T229 376L231 369Q231 367 232 367L243 378Q303 442 384 442Q401 442 415 440T441 433T460 423T475 411T485 398T493 385T497 373T500 364T502 357L510 367Q573 442 659 442Q713 442 746 415T780 336Q780 285 742 178T704 50Q705 36 709 31T724 26Q752 26 776 56T815 138Q818 149 821 151T837 153Q857 153 857 145Q857 144 853 130Q845 101 831 73T785 17T716 -10Q669 -10 648 17T627 73Q627 92 663 193T700 345Q700 404 656 404H651Q565 404 506 303L499 291L466 157Q433 26 428 16Q415 -11 385 -11Q372 -11 364 -4T353 8T350 18Q350 29 384 161L420 307Q423 322 423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 181Q151 335 151 342Q154 357 154 369Q154 405 129 405Q107 405 92 377T69 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path>
<path stroke-width="1" id="E1-MJMATHI-64" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path>
</defs>
<g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)" aria-hidden="true">
 <use xlink:href="#E1-MJMATHI-63" x="0" y="0"></use>
 <use xlink:href="#E1-MJMATHI-6D" x="433" y="0"></use>
 <use xlink:href="#E1-MJMATHI-64" x="1312" y="0"></use>
</g>
</svg> 的标准输出可以直接赋值给变量，获取指令输出非常方便。当使用 <svg xmlns:xlink="http://www.w3.org/1999/xlink" width="4.263ex" height="2.176ex" style="vertical-align: -0.338ex;" viewBox="0 -791.3 1835.5 936.9" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" aria-labelledby="MathJax-SVG-1-Title">
<title id="MathJax-SVG-1-Title">cmd</title>
<defs aria-hidden="true">
<path stroke-width="1" id="E1-MJMATHI-63" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path>
<path stroke-width="1" id="E1-MJMATHI-6D" d="M21 287Q22 293 24 303T36 341T56 388T88 425T132 442T175 435T205 417T221 395T229 376L231 369Q231 367 232 367L243 378Q303 442 384 442Q401 442 415 440T441 433T460 423T475 411T485 398T493 385T497 373T500 364T502 357L510 367Q573 442 659 442Q713 442 746 415T780 336Q780 285 742 178T704 50Q705 36 709 31T724 26Q752 26 776 56T815 138Q818 149 821 151T837 153Q857 153 857 145Q857 144 853 130Q845 101 831 73T785 17T716 -10Q669 -10 648 17T627 73Q627 92 663 193T700 345Q700 404 656 404H651Q565 404 506 303L499 291L466 157Q433 26 428 16Q415 -11 385 -11Q372 -11 364 -4T353 8T350 18Q350 29 384 161L420 307Q423 322 423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 181Q151 335 151 342Q154 357 154 369Q154 405 129 405Q107 405 92 377T69 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path>
<path stroke-width="1" id="E1-MJMATHI-64" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path>
</defs>
<g stroke="currentColor" fill="currentColor" stroke-width="0" transform="matrix(1 0 0 -1 0 0)" aria-hidden="true">
 <use xlink:href="#E1-MJMATHI-63" x="0" y="0"></use>
 <use xlink:href="#E1-MJMATHI-6D" x="433" y="0"></use>
 <use xlink:href="#E1-MJMATHI-64" x="1312" y="0"></use>
</g>
</svg> 调用指令时，必须指定异常处理，可以通过 <code>failed</code> 关键字指明之后的代码块用作异常处理。<code>fail</code> 关键字用于抛出异常。</p>
<p>还有一种简化的一场处理方式，上面代码可以稍作调整:</p>
<pre><code class="hljs lang-xquery">    <span class="hljs-keyword">let</span> file_content = ${cmd} {file_name}$?
</code></pre><p>相当于</p>
<pre><code class="hljs lang-xquery">    <span class="hljs-keyword">let</span> file_content = ${cmd} {file_name}$ failed {
        fail status
    }
</code></pre><p>我很喜欢 <code>?</code> 的设计，表示指令是不可靠的，出错时就停止。二者的区别是简写方式无法指定报错信息。</p>
<h1><a id="toc-25f" class="anchor" href="#toc-25f"></a>总结</h1>
<p>目前我已用 amber 作为 Linux 下编写脚本的首选工具，感觉好用。好工具，好生活，拯救脱发。</p>

            ]]></description>
            <pubDate>Wed, 29 May 2024 17:25:14 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/amber_script.html</guid>
        </item>
        <item>
            <title>如何实现可动态扩展的共享内存池</title>
            <link>http://www.thinkinpython.com/post/shmPoolImplement.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-e56">共享内存的原理</a></li>
<li><a href="#toc-bf7">抽象共享内存指针</a></li>
<li><a href="#toc-544">动态扩展</a></li>
<li><a href="#toc-25f">总结</a></li>
</ul>
</div><p>开发者通常为了提高内存使用效率，或者避免内存泄漏，需要将内存池化管理。稍复杂一点的系统，一般都会有自己的内存管理机制，我在研读源码的时候，比较习惯先看内存管理模块的实现，这块很见开发者的基本工和工程思想，颇有一叶知秋之感，一个内存管理一团乱麻的系统，注定不会是艺术品。
内存池的实现方法很多，但万变不离其宗，通常就是一次申请大块内存，进程再将其化整为零的重复使用，对于操作系统来说，只发生了少数几次内存分配调用，避免长时间运行后内存碎片化。当内存池耗尽时，可以再申请一大块内存入池，实现内存池扩容。比较常见的是进程私有内存池，共享内存如何实现池化呢？</p>
<h1><a id="toc-e56" class="anchor" href="#toc-e56"></a>共享内存的原理</h1>
<p>我们通常认为的内存地址，实际上并不是物理内存上的位置。操作系统出于安全性和效率考虑，每个进程都有独立的虚拟地址空间，A 进程的 0X123 和 B 进程的 0X123 处的数据通常没有任何关系。</p>
<p>本文均以 X64/X86 架构为例。</p>
<p><img src="https://thinkinpython.com/static/upload/20240528/upload_63f94905c3e25388d89b4c161e7951f3.png" alt="image-1.png"></p>
<p>Linux 系统中，所有进程看到的地址空间大体如图，低地址处通常为用户程序，高地址处为内核空间。这个地址空间在不同 CPU 下也有一些差异：</p>
<ul>
<li>32bit 系统中，虚拟地址空间总大小 4G，其中 1G 为内核区域。</li>
<li>64bit 系统中，只使用了 48 位地址，因此虚拟地址空间总大小为 256T，通常 8T 为内核区域。</li>
</ul>
<p>64bit 系统的的进程虚拟地址空间非常大，普通计算机只能用到其中一小部分，操作系统通过 MMU 将虚拟地址映射到真实的物理内存上，这样只需要很小内存，也能同时运行大量程序。</p>
<p><img src="https://thinkinpython.com/static/upload/20240528/upload_e0f99182b4f976585a9932f321de17ad.png" alt="image-2.png"></p>
<p>在虚拟内存中地址连续的页面，在物理内存上的地址是不确定的，甚至在不同时刻，相同的虚拟内存地址也会被映射到不同的物理地址。</p>
<p>如果把同一块物理内存映射到两个进程的虚拟地址会发生什么呢？</p>
<p><img src="https://thinkinpython.com/static/upload/20240528/upload_0ad0449cfd90547501d3e74157706309.png" alt="image-3.png"></p>
<p>任何一方修改这块区域，另一方都能立刻看到，因为它们实际上是相同的物理内存。但是请注意，这块物理内存区域在两个进程的虚拟内存中的地址却不一定相同。除非两个进程有亲缘关系，一个进程从另一个进程完整的继承了共享内存的映射关系，否则一般来说，同一块共享内存在不同进程中的地址是不同的。</p>
<h1><a id="toc-bf7" class="anchor" href="#toc-bf7"></a>抽象共享内存指针</h1>
<p>对于没有亲缘关系的进程们，同一块共享内存通常会映射到各自虚拟内存的不同地址。显然进程之间无法共享虚拟内存地址，我们需要一种通用方法，描述共享内存中的位置。其实内存地址本质上是指针到 0x00 的偏移，我们可以稍作修改，创造一个全新的概念 “共享指针”，它也是一个偏移，但其基准地址是这块共享内存的虚拟内存地址。</p>
<p><img src="https://thinkinpython.com/static/upload/20240528/upload_14cc69a68bb8d4a8d9d74f4b1bda6cf1.png" alt="image-4.png"></p>
<p>无论在哪个进程，我们都可以通过共享指针，即相对共享内存起始位置的 offset 定位到相同位置，就算各进程共享内存的映射地址（起始地址）不同也没所谓。</p>
<p>很容易得到这样的公式：</p>
<pre><code class="hljs lang-undefined">共享指针的在进程中的真实地址 = 共享内存基址 + 共享指针（也就是 offset）；
共享指针 = 共享指针的在进程中的真实地址 -  共享内存基址；
</code></pre>
<p>只要将共享内存中所有指针都改为 “虚拟指针”，那么所有进程都可以将 “虚拟指针” 转换为自己虚拟内存中的地址正确访问。</p>
<p><img src="https://thinkinpython.com/static/upload/20240528/upload_a46f02b55ba3cf4d9ba994e79d71c7f6.png" alt="image-5.png"></p>
<p>共享内存就可以被切一组 block，通过一些 “共享指针”，把空闲 block 管理起来。</p>
<h1><a id="toc-544" class="anchor" href="#toc-544"></a>动态扩展</h1>
<p>Linux 系统常见的 3 中动态内存接口：</p>
<ul>
<li><p>POSIX </p>
<ul>
<li>shm_open 函数创建。</li>
<li>ftruncate 设置大小。</li>
<li>通过mmap 将其映射到进程的地址空间，返回一个指针。</li>
<li>munmap 解除映射。</li>
<li>shm_unlink 删除共享内存对象。</li>
</ul>
</li>
<li><p>System V</p>
<ul>
<li>shmget 函数。</li>
<li>shmat 将共享内存段附加到进程的地址空间，返回一个指针。</li>
<li>shmdt 从进程的地址空间分离该共享内存。</li>
<li>shmctl 支持其他操作，如删除共享内存段。</li>
</ul>
</li>
<li><p>mmap 匿名内存映射</p>
</li>
</ul>
<p>无论哪种方式，都无法自动调整共享内存大小，有没有可能创建可以动态调整大小的共享内存池呢？前一步我们已经可以将一块共享内存按 block 管理起来，我们只需要再多申请一块共享内存，把它切分后就是一些 block 了。当所有进程都可以看到第一块共享内存的时候，如何找到新增的共享内存呢？</p>
<p><img src="https://thinkinpython.com/static/upload/20240528/upload_970a6f7747ebd62d75b7bfda5afc8240.png" alt="image-6.png"></p>
<p>我们只需要在每块共享内存的特定位置，比如开始处，保存下一个共享内存的挂载参数，SystemV 接口就保存 shm key 和 size，POSIX 就保存 name 和 size，用全 0 值表示结束。 逻辑与单向链表类似，只不过通过共享内存参数而非指针串联起来。</p>
<p>前面我们引入一层抽象：“共享指针”，以统一的形式对所有进程描述共享内存中的位置，实际上就是共享内存中的 offset。现在情况变得复杂一点，单纯依靠 offset 已经不足以定位共享内存中的位置，还需要区分是在哪个共享内存上。是时候让 “共享指针” 变得复杂一些。</p>
<p>如果你对网络地址比较熟悉，应该听说过 “子网掩码”，我们也可以将 “共享指针” 的地址空间划分为共享内存编号 + offset。</p>
<p><img src="https://thinkinpython.com/static/upload/20240528/upload_8c4e6edf6dd8d01ba98427212e23e91a.png" alt="image-7.png"></p>
<p>以 64 位地址为例，offset 只需占据 40 位就可以支持 1T 大小的单块共享内存，以目前的硬件内存价格来说，已经是相当充足了，剩下 24 位作为共享内存编号使用，支持管理 16M 个共享内存块，也相当富余。理论上说，“共享指针” 的这两部分构成比例，决定了管理较少但更大的共享内存块，或者更小但更多共享内存块。</p>
<p><img src="https://thinkinpython.com/static/upload/20240528/upload_48475947e9eacd816c4d60c26cbdb4d5.png" alt="image-8.png"></p>
<p>比如一个 “共享指针” 如图，它指向位于 1 号共享内存起始位置之后 32Bytes 位置的一块区域。共享内存中记录这些 “共享指针”，像一般指针一样构建复杂的结构。所有进程只需将 “共享指针”转换为自己的虚拟内存地址，就可以正确访问。</p>
<h1><a id="toc-25f" class="anchor" href="#toc-25f"></a>总结</h1>
<p>只要 shm 中全部使用 “共享指针”，就可以扫清多进程共享内存地址映射的不一致性障碍，以很小的代价移植常见的内存管理方案。</p>

            ]]></description>
            <pubDate>Tue, 28 May 2024 15:20:18 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/shmPoolImplement.html</guid>
        </item>
        <item>
            <title>超纲词汇检查工具使用说明</title>
            <link>http://www.thinkinpython.com/post/englishWordCheck.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-b4d">文件清单</a></li>
<li><a href="#toc-aaa">使用要求</a></li>
<li><a href="#toc-ec0">使用方法</a></li>
</ul>
</div><h1><a id="toc-b4d" class="anchor" href="#toc-b4d"></a>文件清单</h1>
<p>压缩包中包含：</p>
<ul>
<li>可执行程序 EnglishWordCheck.ext</li>
<li>示例单词清单：english_words1.txt</li>
<li>示例 word 文档：sample.doc</li>
</ul>
<h1><a id="toc-aaa" class="anchor" href="#toc-aaa"></a>使用要求</h1>
<ol>
<li>单词清单文件</li>
</ol>
<p>文件名自定，文件类型为 txt。可以为不同的大纲分别准备单词表。</p>
<p>内容为大纲单词，各单词以空格、tab、换行分割，空白数量不限。</p>
<p>连接符连载一起的单词 <code>A-B</code> 识别为一个单词。</p>
<p>不区分大小写。</p>
<p>例如以下几种都是可以的：</p>
<pre><code class="hljs lang-undefined">apple
banana
peach
</code></pre>
<pre><code class="hljs lang-undefined">apple   banana                   peach
</code></pre>
<pre><code class="hljs lang-undefined">apple   banana
                   peach
</code></pre>
<ol start="2">
<li>检测文件</li>
</ol>
<p>仅支持  <code>docx</code>。</p>
<p>工具会检查 doc 中所有英文单词是否属于 1 中定义的大纲词汇。</p>
<p>单复数、过去时、ing 等都会自动转换为基本形式比较。也就是说单词清单中词汇的所有事态、型格都会被认为属于大纲内词汇。</p>
<h1><a id="toc-ec0" class="anchor" href="#toc-ec0"></a>使用方法</h1>
<p><img src="https://thinkinpython.com/static/upload/20240422/upload_cd9e3dd49136e25788fc41a0d44d1b08.png" alt="image.png"></p>
<p>按步骤选择词汇表、word 文档，点击 check 即可。</p>
<p>超纲词汇报表会输出在 word 文档同级目录，与 word 文档同名的 xlsx 文件中。</p>
<p><img src="https://thinkinpython.com/static/upload/20240422/upload_333da0add9a9ce2d83e0890639d1ee36.png" alt="image.png"></p>

            ]]></description>
            <pubDate>Mon, 22 Apr 2024 04:46:37 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/englishWordCheck.html</guid>
        </item>
        <item>
            <title>快速离线部署 LLama2</title>
            <link>http://www.thinkinpython.com/post/gpt4all_install.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-fee">安装 GPT4All</a></li>
<li><a href="#toc-d14">安装模型</a></li>
<li><a href="#toc-fa1">进阶</a></li>
<li><a href="#toc-25f">总结</a></li>
</ul>
</div><p>今年 AI 发展迅速，尤其是 LLM 涌现出了爆款应用 “ChatGPT”，不会整两句 GPT 你都不好意思扫共享单车。但是由于 OpenAI 的服务限制，国内访问比较困难。再加上自己的问题多少会有一些隐私问题，如果让云厂商知道我账户里的好几千巨款，心里不踏实。信息放在云端远不如运行在本地放心。</p>
<p>感谢伟大的开源社区，Meta 开源了 Llama2 模型，据社区测评，在多数维度可以接近 ChatGPT 的效果。后来 Github 有热心大神开发了 llama.cpp，让 LLM 运行在本地 CPU 上成为现实。llama.cpp 项目的编译安装都非常简单，如果你是个程序员，会很熟悉 <code>./configure &amp;&amp; make &amp;&amp; make install</code> 三部曲。从 llama.cpp 开始折腾还是需要点耐心，想快速尝鲜的话，推荐 GPT4All 这款应用。大概看了下，它应该就是 llama.cpp 的 UI 版。</p>
<h1><a id="toc-fee" class="anchor" href="#toc-fee"></a>安装 GPT4All</h1>
<p>GPT4All 支持 Linux、Mac、Windows 平台，前往官网 <a href="https://gpt4all.io/index.html">https://gpt4all.io/index.html</a> 下载安装适合自己平台的版本。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_452362d577f73513f8d80010e9799e4e.png" alt="image.png"></p>
<p>如果需要设置代理和本地缓存目录，请点击 Setting。点击下一步。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_d48fd43f0317f627c818ecde318813f5.png" alt="image.png"></p>
<p>请浏览选择安装路径，因为我打算把模型文件下载到 GPT4All 子目录，一般一个中小型模型大约在 10G 左右，所以最好安装磁盘剩余空间充足。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_9bfb69f6a3670ff4fee148ebf2c1bd6e.png" alt="image.png"></p>
<p>点击下一步。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_36a716e1e0057700d37cbf9333ae5bdf.png" alt="image.png"></p>
<p>每个软件都有的许可协议，勾选同意，继续下一步。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_ef6e37c9841a9c4f509f4a8d666fdba8.png" alt="image.png"></p>
<p>点击 Install，正式开始安装。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_8048a1a84578da8b3d37de3e7177d025.png" alt="image.png"></p>
<p>安装过程需要十分钟左右，取决于你的网速。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_af65de68bb604fd90caee51e743cf3a8.png" alt="image.png"></p>
<p>安装完毕。</p>
<h1><a id="toc-d14" class="anchor" href="#toc-d14"></a>安装模型</h1>
<p>GPT4All 内置了一些训练好的模型，可以通过 GPT4All 下载使用。</p>
<p>这里先说一下如何打开 GPT4All，Mac 下安装好后会看到这两个图标：</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_243e34b46f3b00329b5b5d5f653df647.png" alt="image.png"></p>
<p>请点击右侧图表 gpt4all 启动。左侧为管理工具。</p>
<p>Windows 下开始菜单里似乎只有左边的 maintenance tool，GPT4All 的本体需要去安装目录/bing/ 下找 chat.ext。总之非常诡异，可能这就是所谓的 “工程师文化”，不管你用户的死活。 </p>
<p>如果你顺利的找到 GPT4All 的启动图标，第一次启动会看到这个页面，提示你下载模型文件。这是因为 GPT4All 只是模型框架，需要模型文件作为初始化参数。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_c6c4ead4e6158549a79f87063dec40c0.png" alt="image.png"></p>
<p>亲测 OpenOrca 还可以，其他部分模型准确度很差。</p>
<p>下载前，点击最下面的 Browse，重新选择模型保存目录，为了方便，请在 GPT4All 安装目录下新建 models 目录，并将其选定为模型下载目录。</p>
<p>点击 OpenOrca 的 download，耐心等待下载完毕。</p>
<p><img src="https://thinkinpython.com/static/upload/20231221/upload_6c8586eda809bc0af539b9488cda399b.png" alt="image.png"></p>
<p>下载完毕就可以进入对话，与 AI 畅聊。由于这些模型的训练输入主要是英语，所以对中文支持不怎么样。虽然是基于 CPU 实现推理，但也能达到 5 Tokens 每秒，还在可以用的程度。</p>
<h1><a id="toc-fa1" class="anchor" href="#toc-fa1"></a>进阶</h1>
<p>前面提到 GPT4All 应该是 llama.cpp 的 UI 版，所以它也支持的模型类型和 llama.cpp 是一样。除了从 GPT4All 自带的模型库选择不多，我们也可以自己从 huggingface 找到大量训练好的模型，放到前面设置的模型下载目录 models，GPT4All 也可以加载运行。</p>
<p>注意，模型需为 gguf 格式。</p>
<p>huggingface 访问速度很慢，模型又动则七八 G，推荐一个国内镜像 <a href="https://hf-mirror.com/">https://hf-mirror.com/</a> 下载速度可达 MB/s。</p>
<h1><a id="toc-25f" class="anchor" href="#toc-25f"></a>总结</h1>
<p>试用了三四个模型，包括一个国内某大学训练的中文模型，与 ChatGPT 还有很大的差距，应该是我的电脑能力有限，最多只能跑 7B 的 llama，再加上社区训练的模型数据量和调优没法和 OpenAI 匹敌，果然 AI 时代，数据为王。如果想在本地获得比较好的使用效果，还需要针对自己的需求继续调教模型。现阶段，个人使用还是推荐云厂商的 GPT 产品，数据更新及时，有超算支撑，使用体验更好。</p>

            ]]></description>
            <pubDate>Wed, 20 Dec 2023 16:11:28 GMT</pubDate>
            <guid>http://www.thinkinpython.com/post/gpt4all_install.html</guid>
        </item>
    </channel>
</rss>
