AirLLM逐层加载原理与实操:8GB显存跑70B大模型
我第一次看到 AirLLM 这个名字的时候第一反应是又一个标题党项目。让 70B 参数规模的大模型跑在 8GB 显存的消费级显卡上这话放在任何一个技术社区里都像卖课广告。但把这个项目的源码和实现思路从头到尾翻了一遍之后我得承认它确实有点东西。AirLLM 没有发明什么新的数学公式也没有把模型压缩成一只小钢炮它只做了一件事把你自己早就拥有的那块显卡用得更狠了一点。这篇文章我会从显存账单算起把 AirLLM 的逐层加载原理拆开讲清楚再给出一套可以直接抄作业的实操流程。最后我会聊一聊实测里踩过的坑以及它在什么场景下真的值得用。1. AirLLM 到底在解决什么问题先算清这笔显存账1.1 为什么大模型像一只吞显存的怪物很多人对大模型显存需求的第一印象是“模型很大”但“大”到什么程度大部分人没算过细账。以 70B 参数规模的开源模型为例如果用 FP16 精度保存权重每个参数占 2 字节光权重就是 70 × 2 140GB。这还没算推理时要做的事情。推理时显存主要消耗在三个地方权重本身、KV Cache缓存历史 token 的键值对、以及中间激活值。KV Cache 的大小跟序列长度成正比序列越长越夸张。一个 70B 模型在输入 2048 token 时KV Cache 轻松吃掉几个 GB。中间激活值虽然每个 Batch 结束就会释放但峰值同样不可忽视。三个加起来一块 24GB 显存的卡根本不够看常规做法是上 80GB 甚至更高的专业卡。我见过不少同学买卡前只盯着“模型权重多少 GB”上车之后才发现推理时显存根本不够用。这是个非常经典的误区推理显存需求从来不是权重大小这一个变量决定的。1.2 传统路线的三板斧量化、切分、蒸馏面对显存墙社区里常见的解决思路有三条。第一条是量化把 FP16 权重降到 INT8 或者 INT4显存需求直接砍半甚至砍到四分之一。但 70B 模型即使在 INT4 下仍然要 35GB 左右还是超出消费级显卡的承载范围。第二条是多卡切分把模型按层或按张量切成几份分散到多张卡上。这需要你有两张以上的卡而且要考虑通信开销消费级玩家大多不具备这个条件。第三条是蒸馏和裁剪用大模型产出训练数据去训一个小模型或者把大模型剪成小模型。这条路最实用但本质上你已经不是在使用那个 70B 的模型了输出质量和覆盖能力都会有损耗。AirLLM 走的是完全不同的第四条路不缩小模型、不切模型、也不换小模型而是改变模型在显存里的“居住方式”。1.3 AirLLM 一句话概括AirLLM 的核心思路是逐层加载用完即走。Transformer 模型是由几十层结构几乎相同的模块堆叠而成的推理时数据必须从第一层流到最后一层这个过程天然是串行的。AirLLM 利用这一点让 GPU 某一时刻只装载当前正在计算的那一层的权重算完立刻腾空再装载下一层。这样显存峰值就从一个“完整模型”的体量降到了“单层权重加一点中间结果”的体量。90 层、140GB 的模型被拆成 1 层、1.5GB 左右的单次负载一块 8GB 显卡当然装得下。提示AirLLM 解决的是推理时的显存瓶颈不是模型下载体积。权重文件该多大还是多大它占的是你的硬盘和 CPU 内存不是显存。2. 逐层加载为什么成立原理拆解与性能代价2.1 Transformer 的结构给了 AirLLM 一条活路要理解 AirLLM 为什么能这样设计得先看 Transformer 的推理流程。输入的 token 经过 Embedding 层变成向量然后依次穿过每一层 Transformer Block每一层都做同样的操作层归一化、多头注意力、前馈网络最后再进下一层。上一层不结束下一层就无法开始。这意味着在整个 forward 过程中除了当前层其他层的权重躺在显存里纯粹是“占着茅坑不拉屎”。常规框架不会去动这些闲置权重因为“把权重搬进来搬出去”本身有开销对专业卡来说不值得。但 AirLLM 瞄准的场景恰恰是显存小到放不下整个模型的情况此时“搬来搬去”就不是开销而是唯一的出路。生活化类比你要在一张只能容纳一个人的桌子上完成一套流水线作业——桌上只摆当前工序需要的那份材料做完一份收走一份再摆下一份。桌子不需要变大工序照样能完成只是多花点时间在“摆材料”上。2.2 一次推理的显存流转全过程我用一个 70B 模型、FP16 权重、90 层来模拟 AirLLM 的推理过程。假设最终要生成 256 个 token实际执行时是逐 token 生成的但每一轮生成都要走一遍全部层。单轮生成的内存流转如下输入 token 被切成长度上限内的序列在 CPU 内存中完成 tokenizer 转换。GPU 初始化一小块缓冲区装载 Embedding 层权重计算输入向量。加载第 1 层权重到显存执行该层的 forward计算结果和 KV 缓存保留在显存或统一管理的内存区域。第 1 层权重释放。加载第 2 层权重重复上面两步直到最后一层。最后一层输出经过 LM Head 映射为词表概率采样出一个新 token。新 token 拼回输入序列开始下一轮生成。整个过程中显存峰值大概是“单层权重 KV Cache 增量 少量激活值”的量级。以常见开源 70B 模型每层约 1.5GBFP16来估算再加上几 GB 的 KV 和激活一块 8GB 显存的卡确实可以跑这就是 AirLLM 最核心的工程事实。2.3 实际代价带宽、延迟与吞吐逐层加载不是没有代价。最大的代价是 CPU 与 GPU 之间的 PCIe 传输。每次换层都要把新权重从 CPU 内存拷进显存90 层就是 90 次拷贝。PCIe 4.0 的实际带宽大概在 10~20GB/s 左右而 CPU 内存的读取还要受限于内存带宽和 NUMA 拓扑。粗算一下每层 1.5GB 拷进拷出单层耗时约 0.1 秒以上90 层一轮生成就是 10 秒以上的传输时间这还没算 GPU 实际计算的时间。所以 AirLLM 的速度注定不会快。我实测下来70B 模型在消费级显卡上生成一个 token 通常要几十秒甚至更久远不能和云端专业卡相比。但它的意义在于你把成本门槛从几十万的服务器降到了一张游戏卡换来的是可以接受但需要耐心的生成速度。对于验证想法、跑实验、做离线推理这个交换是划算的。2.4 为什么不对 KV Cache 也做同样处理有人可能会问KV Cache 能不能也逐层加载严格说也可以但 KV Cache 是逐 token 累积的每一层保留的 cache 在下一轮生成时都要继续用。如果全部放 CPU 内存每轮生成把 90 层的 KV 都搬进搬出传输量会暴涨。AirLLM 这类方案一般会把 KV Cache 留在显存或统一内存中只有权重走逐层加载的路线这是实践中平衡速度和显存的关键取舍。3. 照着做就能跑环境准备与最小推理代码3.1 你需要准备什么AirLLM 对硬件的要求其实很低但有几条硬性条件要满足显卡支持 CUDA 的 N 卡显存至少 4GB推荐 8GB 及以上。A 卡用户建议先放弃目前生态支持不足。内存CPU 内存建议 32GB 起步因为模型的全部权重都要常驻 CPU 内存70B FP16 模型需要约 140GB所以 128GB 内存 70B 模型是比较合理的组合跑 7B 或 13B 则 32GB 内存够用。硬盘模型文件动辄几百 GBSSD 基本是必需品否则加载过程会让人崩溃。软件Python 3.10 左右安装 PyTorch 的 CUDA 版本然后通过包管理器安装 airllm 及相关依赖。安装本身不复杂但建议用独立的虚拟环境避免污染其他项目。我第一次就是直接在全局环境里装结果版本冲突解了一下午。3.2 最小推理代码下面是能跑通的最短路径代码里我用一个泛化的模型路径代替实际模型名import torch from airllm import AutoModel model_path /path/to/local/model # 换成你自己的模型目录 # 加载模型compression 参数控制权重压缩 model AutoModel.from_pretrained( model_path, compression4bit, # 可选 2bit / 4bit / 8bit / none max_length1024, # 控制最大序列长度显存不够就调小 devicecuda # 主计算设备 ) model.eval() prompt 用三句话解释什么是注意力机制。 inputs tokenizer(prompt, return_tensorspt).to(cuda) with torch.no_grad(): output_ids model.generate( inputs.input_ids, max_new_tokens128, do_sampleTrue, temperature0.7 ) print(tokenizer.decode(output_ids[0], skip_special_tokensTrue))这段代码看起来和普通 Hugging Face 风格的推理几乎一样唯一区别是 AutoModel 来自 airllm 包。加载时模型不会把全部权重拷进显存而是把权重映射到 CPU 内存仅在计算时逐层加载。3.3 参数怎么调compression、max_length 与 batch sizecompression是最重要的参数。它决定权重在 CPU 内存里的存储精度。跑 70B 模型时我建议直接用 4bit显存压力小。跑 13B 或 7B 模型时可以用 8bit精度损失更小。如果你显存足够也可以设成 none 保持 FP16速度会更快因为省掉了压缩和解压的步骤但显存需求会回升到“单层 FP16 权重”的水平。max_length决定了 KV Cache 的上限。显存不够 OOM 时第一件事就是调小它。实测里很多人把 max_length 设成 2048 或 4096结果显卡只有 8GB生成到一半就崩了。我的建议是先从 512 起步跑通之后再逐步加大。batch size 基本只支持很小的值因为 AirLLM 的逐层加载设计就是为单条推理服务的。想要批量并发等于是同时跑几个逐层加载流程显存依然不够。这也是它和常规推理框架最大的体验差异它是单用户、单请求的推理方案。3.4 提速的几个工程手段逐层加载最大的瓶颈是“拷权重”所以一切优化都围绕减少等待展开开启异步预取如果项目支持开启异步预取后GPU 计算第 N 层时CPU 同时把第 N1 层权重拷到显存缓冲区传输时间被隐藏掉一部分。这一步通常能把端到端速度提升 30% 甚至更多。减少 CPU 内存拷贝次数尽量把模型文件放在内存盘或使用大页内存托管的区域减少缺页中断。调低生成参数关闭采样、固定贪婪解码会让每轮生成的可变开销变小速度更稳定。不要开太多后台程序AirLLM 非常依赖 CPU 内存带宽后台的浏览器标签页、编译任务都会抢占内存带宽实测速度能差一倍。注意“拷权重快不快”很大程度上取决于 CPU 内存带宽和 PCIe 带宽。这两项在消费级平台上是固定硬件参数软件层面能优化的空间有限。所以如果你对速度有硬性要求升级内存通道数或直接上职业卡比调参更有效。4. 跑起来之后常见报错、排查与提速技巧4.1 模型加载过程中直接 OOM 或卡死这个问题的根源通常不在显存而在 CPU 内存。70B 模型 FP16 需要 140GB 内存如果你只有 32GB加载时内存就爆了。解决办法是配合量化使用AirLLM 支持加载前先在磁盘上完成权重压缩再映射进内存。用 4bit 压缩后 70B 模型大约只需 40GB 内存仍在多数桌面平台的承受范围内。另一个常见原因是硬盘太慢。第一次加载模型需要把几百 GB 从磁盘读进内存机械硬盘基本上是灾难NVMe 固态都要等几分钟到几十分钟不等。这不是 bug是数据量本身的物理限制。4.2 生成速度慢到怀疑人生AirLLM 推理慢是常态但如果你发现慢得离谱比如一个 token 超过两三分钟优先检查以下几个方面。第一查看进程 CPU 占用率。如果 CPU 占用只有个位数大概率是卡在磁盘读取上模型没有完全载入内存。把整个模型目录先复制到内存足够充裕的情况下或者改用更快的大容量 SSD。第二检查 PCIe 链路是否工作在 x16。有些主板在插入多张卡或 M.2 设备后会自动降速PCIe 带宽减半对逐层加载是致命的。第三确认是否真的启用了异步预取。文档里开关只是环境变量或构造参数的事很多人漏了这一步实际跑的还是同步加载模式。4.3 输出质量明显变差压缩到 4bit 甚至 2bit 之后模型质量必然有退化这是绕不开的物理规律。但如果你发现退化特别严重比如生成结果出现大量乱码多半是压缩算法和目标模型不兼容。AirLLM 在处理某些模型结构时可能走的是通用量化路径个别层对精度极其敏感。我的经验是先切到 8bit 对比一下。如果 8bit 正常、4bit 崩坏那就是压缩问题而不是模型问题。若必须用 4bit试着把max_length调小因为长序列下量化误差会通过层间传播被放大。4.4 长文本生成中途崩溃长文本生成最怕两件事KV Cache 撑爆显存以及多轮对话时历史上下文叠加导致内存膨胀。AirLLM 在长文本下会反复执行逐层加载每多一个 tokenKV Cache 就大一圈。如果你生成到中途出现 CUDA OOM先把 max_length 降下来或者开启自动截断历史的选项。还有一个我踩过的坑多轮对话时把每一轮的完整历史都拼进输入导致输入序列越来越长最终触发 max_length 限制。解决办法是只保留最近几轮对话并手动拼接成新的 prompt。5. 选型判断AirLLM 适合什么场景不适合什么场景5.1 和其他方案的对比视角总结一张表方便你根据自己情况做判断对比项AirLLM逐层加载量化推理单卡多卡张量并行API 调用云端最低硬件门槛消费级游戏卡消费级游戏卡两张以上消费卡无硬件但需网络与费用能否跑 70B 级模型能显存够也行不够不行能能推理速度慢分钟级响应快很多较快取决于云端实例输出质量取决于压缩精度取决于压缩精度无损无损主要成本一次性硬件电费一次性硬件电费硬件成本更高长期按量付费典型场景离线实验/私有推理在线小模型服务专业自建推理生产级业务AirLLM 不是来取代任何方案的它补的是“没有专业卡、又想跑大模型”这个夹缝需求。5.2 我推荐的三个适用场景第一个是算法验证和教学实验。你想验证某个 70B 模型在具体任务上的表现不想花大钱租卡又不想用蒸馏小模型代替AirLLM 可以让你在本机跑通完整链路。慢是慢一点但结果可信。第二个是数据准备的离线推理。比如给一批文本打标签、做批量摘要这些任务不追求实时响应深夜挂着跑一晚上第二天早上收结果完全可行。第三个是隐私敏感的本地推理。数据不能出本机的情况下AirLLM 让你用消费级硬件完成大模型的私有部署避免了数据上传到云端带来的合规问题。5.3 不建议用的场景生产环境的高并发在线推理建议直接划掉。AirLLM 的单条推理已经慢到分钟级并发场景下更是灾难。另一个不建议的是对实时性敏感的交互式应用比如聊天机器人客服用户等 30 秒已经不耐烦等几分钟直接放弃。还有一个容易被忽略的问题长时间高负载推理对 CPU 内存和主板供电的压力不小。我连续跑了几个大任务之后内存温度明显上升机箱散热器噪音变大。如果你准备拿它跑长时间批量任务记得做好散热和供电余量。写在最后的一点实际体验我自己在实测 AirLLM 的时候最直观的感受是它不是一个让你“用得爽”的方案而是一个让你“用得起”的方案。如果你的目标是在一张消费级显卡上真正跑起几十 B 参数的大模型验证想法、处理离线任务、保护数据隐私那它值得进你的工具箱。如果你追求的是速度和吞吐老老实实选量化或者云端别在逐层加载这条路上硬耗。最后分享一个小技巧跑大模型之前先跑一个相同参数量级的极小测试用 10 个 token 验证整条链路能通再放真实任务。逐层加载的模型一旦中途崩溃重新加载的时间和显存压力都是成倍增长的小测试能帮你省掉好几次无谓的等待。AirLLM 这类方案的工程边界往往不在“能不能跑”而在“怎么稳定地跑完”先把稳定性搞定再谈效率。

相关新闻

光模块拆解与高频测试国产化方案全解析

光模块拆解与高频测试国产化方案全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 1:48:22 阅读更多 →
小封装D类功放设计实战:从CSP封装到Layout调试

小封装D类功放设计实战:从CSP封装到Layout调试

最近在一款便携蓝牙音箱的音频链路选型上,我把AW8010AFCR这颗芯片翻来覆去研究了一遍。AW8010AFCR是一颗典型的D类音频功率放大器,CSP-9封装,体积小、效率高,在蓝牙音箱、智能语音设备、便携扩音器这些电池供电产品里很常见。这篇…

2026/10/19 1:48:22 阅读更多 →
酷秒神马9.0源码深度解析:从架构重构到任务调度与缓存设计

酷秒神马9.0源码深度解析:从架构重构到任务调度与缓存设计

先说结论:酷秒神马9.0版2026最新源码系统,从代码层面看,不是一次小修小补的迭代。我拿到这套源码包后做的第一件事不是急着跑Demo,而是直接看目录结构和核心依赖的变化。整套系统的主版本号从8.x跳到9.0,意味着有非兼容…

2026/10/19 1:48:22 阅读更多 →

最新新闻

AI漫剧全流程制作:从剧本到成片的零基础技术路径

AI漫剧全流程制作:从剧本到成片的零基础技术路径

B站有一套 AI 漫剧教程,标题很夸张:全 666 集,198 小时,零基础入门,还允许白嫖。很多人第一反应是标题党,但仔细看它列出的内容板块——剧本、分镜、静帧、视频、配音、剪辑、保持人物一致——这正是目前 A…

2026/10/19 2:22:37 阅读更多 →
小鹏MONA零接管跑山背后:智驾感知、规划与控制全链路解析

小鹏MONA零接管跑山背后:智驾感知、规划与控制全链路解析

零接管跑山登顶背后:小鹏MONA智驾的技术看点、验证方法与安全边界“完全是老司机水平”“零接管登顶”……最近小鹏 MONA 辅助驾驶跑山的讨论,标题一个比一个猛。作为长期关注智驾技术栈的程序员,我第一反应不是情绪,而是三个问题…

2026/10/19 2:22:37 阅读更多 →
豆包+即梦5.0+LibTV:AI短剧多主体一致性的全流程生产链路

豆包+即梦5.0+LibTV:AI短剧多主体一致性的全流程生产链路

做 AI 短剧,最让人头疼的不是画面不够精致,而是角色前后不一致。第一镜里还是长发白衣的女主,第三镜就变成了短发黑裙,观众一眼就能看出是“拼接货”。这次分享的“豆包 即梦5.0 LibTV”组合,核心就是解决多主体一致…

2026/10/19 2:22:37 阅读更多 →
电力系统多源异构数据融合框架:面向网络入侵检测的Python实现

电力系统多源异构数据融合框架:面向网络入侵检测的Python实现

简介:本资源聚焦电力系统网络安全防护,面向具备Python基础与机器学习知识的科研人员、电力系统安全方向研究生及工业控制系统安全从业者,提供一套针对网络入侵检测的多传感器异构数据融合完整方案。资源以1个77KB的docx文档形式交付&#xff…

2026/10/19 2:22:37 阅读更多 →
AI漫剧零基础全流程:从剧本到剪辑的人物一致性实战

AI漫剧零基础全流程:从剧本到剪辑的人物一致性实战

如果你在B站刷到过类似标题,大概第一反应是:又是标题党,点进去之后发现确实是一套几百集的视频,但里面全是凑数的水课。但“AI漫剧”这个方向,最近变化实在太快,快到大多数人都还没意识到,制作一…

2026/10/19 2:22:37 阅读更多 →
Android自定义SeekBar:图片背景与图片进度的实现方案与避坑指南

Android自定义SeekBar:图片背景与图片进度的实现方案与避坑指南

简介:面向Android开发者的SeekBar定制方案资源包,以图片替换系统原生样式,解决默认SeekBar外观单一、难以匹配界面整体设计的问题。压缩包内是一个完整的Eclipse工程示例,演示如何继承SeekBar并编写MySeekBar类,在attr…

2026/10/19 2:21:37 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →