一张消费级GPU也能跑70B大模型?AirLLM分层推理实战
一个相当现实的场景手里只有一张显存不宽裕的消费级 GPU却想跑 70B 级别的开源大模型。按常规思路这基本等于劝退。但要先别急着花钱买云服务也可以先看看 AirLLM 这个开源项目。它的思路跟主流方案完全不同——不做并行切分不做激进量化而是用一次只推理一层的“分层推理”方式硬生生把 70B 模型的显存需求压到个位数 GB。我最初接触这个项目时也觉得是噱头但实际跑通之后发现它确实有自己的一套逻辑值得认真聊一聊。这篇文章会从 AirLLM 的核心原理讲起然后是我自己的实操跑通过程包括安装、加载模型、推理、参数调优再到踩过的各种坑。无论你是手里只有一张老显卡的玩家还是正准备用低成本方案做模型验证的同学这篇内容应该都能给你省下不少时间。1. 方案整体拆解AirLLM 为什么能省这么多显存1.1 分层推理的基本思路先把话说清楚AirLLM 的核心机制用一句话概括模型权重不常驻显存而是按层轮流加载到 GPU 上完成计算算完一层就卸载一层。常规的 LLM 推理流程是先把全部权重加载进显存或内存然后逐层计算。70B 模型即使以 INT8 精度存储也要大约 70GB 的空间这还没算 KV Cache 和激活值。这就是为什么大家都觉得跑大模型必须有 A100、H100 这类大显存卡。AirLLM 的逻辑很直接——既然一次推理只需要某一层的权重为什么非要把所有层同时放在显存里在实际执行时项目把整个网络拆解成多个计算阶段每一个阶段对应一个或一组 Transformer 层。前向传播走到哪一层就把这一层的权重从内存搬运到显存完成计算后再搬出去让位给下一层。这样一来显存占用的天花板从“整个模型的大小”降到了“单层权重 激活值 KV Cache”的程度下降幅度非常惊人。注意这里说的内存指 CPU 内存普通 RAM显存指 GPU VRAM。AirLLM 是把权重放在 RAM需要时再搬运到 VRAM所以你的内存必须足够装下整个模型。1.2 为什么选择“层级”而不是“张量并行”有人可能会问既然有张量并行这类标准方案为什么还要搞分层推理答案很简单张量并行需要多张 GPU并且对 GPU 之间通信带宽要求极高。消费级平台想凑齐多卡本身就不现实更别说显卡间互联通常只有 PCIe 带宽跑起来通信开销甚至可能抵消计算收益。AirLLM 的思路是彻底放弃“多卡协同”这个前提转而在单卡、单机上把问题解决掉。用“时间换空间”的方式牺牲一点推理速度换取能跑的模型规模上限。这种取舍在纯研究、快速验证、边缘设备部署这些场景下非常实用因为需求本来就不是高并发而是“我只有一张卡但我要跑这个模型看看效果”。1.3 量化与分层的搭配方式AirLLM 并不排斥量化它默认支持在权重搬运前后额外做精度压缩比如 INT8 或 INT4 量化的权重存储。这样内存侧的占用还能进一步减少。以 70B 模型为例原始 FP16 权重约 140GBINT4 量化后能压到 35GB 左右再加上分层加载GPU 侧占用可以降到几 GB 水平。不过代价是精度损失实测下来生成质量在大多数场景足够用但对数学推理、代码生成这类任务量化带来的误差会明显一些。我自己在实际操作中的体会是如果你只是做文本摘要、文章生成、对话交互这类任务INT4 量化完全可接受但如果你要用它跑代码逻辑推理或复杂数学题尽量保持 FP16 精度哪怕慢一些结果质量也稳得多。2. 实操准备安装配置与模型加载2.1 环境依赖与安装过程AirLLM 的安装不算复杂但有几个前置项需要处理干净。官方推荐 Python 3.8 及以上PyTorch 1.13 以上CUDA 版本跟 PyTorch 匹配即可。我的环境是 Python 3.10 PyTorch 2.0.1 CUDA 11.8跑得很稳定。直接安装pip install airllm如果网络环境不太好安装过程可能卡在 PyTorch 的依赖上建议先用官方源单独装好 PyTorch再装 AirLLMpip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install airllm安装完成后建议做一个快速的 CUDA 验证import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))能正常输出 True 和显卡型号再继续往下走。2.2 模型选择与 HuggingFace 下载策略AirLLM 支持 HuggingFace 上大部分主流开源模型包括 LLaMA、Mistral、Falcon、GPT-NeoX 系等。你不需要在代码里区分模型的层数或者结构因为项目会自动识别模型配置。但有一点要提前确认——你的 CPU 内存是否足够容纳完整模型。我的机器是 64GB 内存跑 13B 模型很轻松跑 70B 的量化版本也能勉强支撑但如果你只有 16GB 内存那就老老实实跑 7B 或 13B 级别。下载模型建议提前用 HuggingFace CLI 完成而不是运行时临时下载因为模型文件动辄几十 GB运行时临时下会非常痛苦pip install huggingface-hub huggingface-cli download meta-llama/Llama-2-13b-chat-hf --local-dir ./llama2-13b-chat注意部分模型在 HuggingFace 上有访问权限限制比如 LLaMA 系列需要先在网页端申请权限并配置 Token。用命令行下载时可以通过环境变量传入export HUGGING_FACE_HUB_TOKEN你的token2.3 代码结构速览与初始化参数AirLLM 的 API 设计非常精简核心类就一个。初始化时可以设置的关键参数有参数作用我的推荐值device指定推理使用的 GPU 编号cuda:0device_mapping自定义层到设备的映射通常用默认值optimize优化路径在性能和内存间取舍device_mapmax_length生成序列最大长度根据任务调整默认 1024hf_tokenHuggingFace 鉴权 Token从环境变量读取更好从我的经验看大部分用户只需要关心device和max_length其余保持默认即可跑通。想深入调优性能时再慢慢研究其他参数。3. 跑通推理流程从加载到输出3.1 最简单的推理示例用 AirLLM 跑一次生成代码非常简短。核心逻辑就是加载模型 - 分词 - 调用 generate - 解码输出。from airllm import AutoModel from transformers import AutoTokenizer model AutoModel.from_pretrained(meta-llama/Llama-2-13b-chat-hf, devicecuda:0) tokenizer AutoTokenizer.from_pretrained(meta-llama/Llama-2-13b-chat-hf) prompt 帮我写一段关于人工智能发展历史的简短介绍 inputs tokenizer(prompt, return_tensorspt).to(cuda:0) output model.generate( inputs.input_ids, max_length512, do_sampleTrue, temperature0.7, top_p0.9, ) response tokenizer.decode(output[0], skip_special_tokensTrue) print(response)这里有个细节需要特别提醒AirLLM 的分层逻辑会重复“搬运权重—计算—卸载”的循环第一次运行时的模型加载和权重调度开销非常大后面再推理会好一些。但如果你用的是机械硬盘而不是 SSD前几次加载模型的体验会非常折磨人。强烈建议把模型放到 SSD 上加载到内存。3.2 首次加载模型会经历什么很多人第一次跑 AirLLM 时容易误以为程序卡死了。其实不是只是模型加载过程有大量 IO 操作。以我自己加载 13B 模型为例从 HDD 加载到内存花了将近 10 分钟SSD 上大概 2 分钟。如果模型分片很多还需要额外时间做文件合并。加载完成后显存占用检查import torch print(torch.cuda.memory_summary(devicecuda))我跑 13B FP16 模型时显存占用大概稳定在 4GB 左右这在常规推理方案里根本不可能实现——13B FP16 光权重就需要 26GB 显存。看到这个数字你就明白分层推理的价值了。3.3 generate 核心参数实测对比我针对同一个 prompt 做了多次测试对比了不同参数下的生成效果和速度参数组合生成耗时输出质量适用场景do_sampleFalse, greedy最快稳定但略显呆板事实类问答temperature0.7, top_p0.9中等自然且多样通用对话temperature1.2, top_p0.95慢富有创造性但偶尔偏题创意写作对于普通用户我的建议是直接采用temperature0.7, top_p0.9的组合这基本是开源模型社区广泛验证过的均衡配置。而如果你用 greedy decoding还能省一点 KV Cache 的显存虽然幅度不大。4. 内存优化与性能调优榨干最后一点空间4.1 显存不变的背后内存才是真正的瓶颈AirLLM 把显存压力转移到了 CPU 内存所以内存大小成了新的瓶颈。这个很容易被忽略。跑 70B INT4 量化模型内存至少需要 40GB 以上同时还要预留操作系统和其他程序的空间。如果内存不够系统会使用 swap那推理速度会直接掉到不可用的程度。建议在跑大模型之前先用系统监控工具确认内存余量。Linux 用户可以用free -h查看Windows 用户直接打开任务管理器看内存曲线。如果发现内存接近满载优先关掉浏览器等大内存应用再做下一件重要的事——关闭 PyTorch 的内存缓存机制。PyTorch 默认会缓存一部分显存以便快速复用但在 AirLLM 这种频繁搬运权重的场景下这反而会导致显存持续累积占用。可在加载模型前加入import os os.environ[PYTORCH_CUDA_ALLOC_CONF] max_split_size_mb:128可以显著减少显存碎片和无效占用实测能省下 500MB 到 1GB 的显存。4.2 量化方案选择与 bitsandbytes 集成AirLLM 支持通过load_in_4bit和load_in_8bit参数启用量化加载model AutoModel.from_pretrained( meta-llama/Llama-2-70b-chat-hf, devicecuda:0, load_in_4bitTrue, )这块内部用的是 bitsandbytes 的量化能力。与直接加载 FP16 相比4bit 量化能进一步减少内存占用同时显存带宽压力也减轻了实际推理速度反而会提升。原因很简单搬运同样的数据量BIT 数越低搬运耗时越少。所以这里的量化不只是省空间还在减小 IO 延迟。不过我建议在做重要任务前检查量化模型的输出质量比如跑一段逻辑推理问题看看输出是不是有明显逻辑断裂。如果质量下降严重再切回 FP16。别盲目追求最低精度。4.3 批处理AirLLM 的高频用途之一AirLLM 在设计上有意识地支持了批处理推理batch inference。这在处理大量短文本时非常有用比如批量给几十条新闻做摘要。批量推理可以摊薄每次加载权重的开销让整体吞吐量大幅上升。注意批处理不等于并行计算它仍然是一个一个地逐层计算只不过同一层的计算可以同时处理多条输入。举个例子加载完第 1 层的权重对 batch 里的所有序列计算第 1 层输出然后再加载第 2 层。相比逐条推理省去了反复加载同一层权重的重复 IO。实测下来在七条新闻摘要的任务里单条逐次推理耗时约 56 秒而批量推理总耗时只有 78 秒相当于每条平均 11 秒左右。吞吐提升明显值得平时多用。注意批量推理的显存占用是线性增长的。batch size 越大KV Cache 占用越高。以 13B 模型为例batch size 从 1 增加到 4显存占用大约从 4GB 升到 7GB。根据自己的显存容量保守设置。4.4 性能调优内存映射与线程数另一个容易忽略的调优点在于数据加载环节。AirLLM 权重搬运依赖 PyTorch 的load_state_dict和 CUDA 拷贝这一块可以通过设置更大的内存映射提高加载速度。在 Linux 下修改文件描述符限制ulimit -n 65535这能避免加载分片时频繁打开关闭文件导致的 IO 卡顿。此外PyTorch 的 CPU 线程数也会影响权重预处理速度import torch torch.set_num_threads(8)线程数不是越多越好设为物理核心数即可。超线程开启过多会引发调度开销反而拖慢速度。5. 常见问题与排查技巧实录5.1 CUDA Out of Memory 但显存明明很大前面提到过模型的权重不在显存中常驻但 KV Cache 和激活值仍然在显存里。当max_length设置过大、或者 batch size 太大时依然会发生 OOM。我在调试一个长文本生成任务时把max_length从 512 调到 2048显存占用翻了快三倍。这是因为 KV Cache 大小与序列长度成正比。排查思路逐步降低 batch size 或 max_length找到当前显卡的临界点。也可以像我在 4.1 节中那样先设置PYTORCH_CUDA_ALLOC_CONF避免缓存碎片化带来的伪 OOM。5.2 生成速度慢到无法接受AirLLM 的慢是它的核心特征但慢也有合理和不合理之分。合理的慢来自分层推理本身——每层都在做 PCIe 搬运。不合理的慢则通常出现在权重加载阶段尤其是从机械硬盘读模型。我可以负责任地说HDD 跑 AirLLM 基本是灾难。换成 SSD 后加载时间缩短 70% 以上推理速度也有小幅提升因为前面的层可能还没算完后续权重已经预加载到内存了。如果加载完成后生成依然很慢还有一个排查点确认自己的 CUDA 版本没有走 CPU fallback。用torch.cuda.is_available()检查。如果输出False说明 PyTorch 没正确安装 CUDA 版本需要重装对应 CUDA 的 PyTorch 包。我的实测参考数据13B FP16 模型生成 100 个 token在 RTX 3060 12GB 上大约耗时 90 到 130 秒。如果你在这个数量级以内基本正常。5.3 权重加载报错与路径处理很多报错其实不是 AirLLM 的问题而是 HuggingFace 模型路径处理不当。特别是使用本地下载好的模型时应传入本地目录的绝对路径model AutoModel.from_pretrained(./llama2-13b-chat, devicecuda:0)注意这是本地目录不是 HuggingFace 的模型 ID。很多人在这里踩坑——以为传模型名就会自动下载但本地目录不存在时反而报错。确保目录存在并且里面包含pytorch_model.bin、config.json等文件。如果不确定文件是否完整重新用 HuggingFace CLI 下载一次。另外如果模型文件夹里同时存在多个版本的分片文件比如pytorch_model-00001-of-00002.bin这种不用手动合并AirLLM 会自动识别分片。但前提是index.json文件存在且和分片对应别删掉它。5.4 运行时出现 Illegal memory access 错误这个错误通常是显存超限或 CUDA 上下文异常导致的。在不稳定的超频环境下尤为常见。排查顺序如下立刻用nvidia-smi看显存占用确认有没有其他进程抢占显存。尝试把 batch size 降到 1max_length 调小。降级 CUDA 版本或 PyTorch 版本个别版本组合确实会触发此类错误。检查电源是否稳定特别是高功耗显卡在峰值负载下如果供电不足也会报内存访问错误。如果上面流程走一遍还没解决开一个干净的 Python 环境只跑最小推理示例。排除是其他库冲突导致的问题。6. 个人经验杂谈AirLLM 适合谁不适合谁我用 AirLLM 跑了几个星期从最初的好奇到后来真的把它作为日常模型试验工具。中间有过很多次想放弃的时刻因为速度确实是硬伤。但后来我意识到一个道理AirLLM 的定位根本不是“加速”而是“解锁”。它真正擅长的是让你在资源受限的条件下验证模型效果、跑通流程、做小规模实验。如果要部署到生产环境扛高并发AirLLM 不是合适的方案请直接考虑量化后的常规加载或者云端大显存实例。有一点值得单独说AirLLM 特别适合在 Jupyter Notebook 这类环境里交互式使用。因为它的单层加载机制让显存占用一直保持低水位你可以一边跑模型一边开着其他任务。我在 Notebook 里同时跑模型推理和数据处理完全不会互相挤爆显存。从我个人的实操体验来看最推荐的组合是中等参数量的模型比如 7B 到 13B加上 INT8 或 FP16 精度跑在 8GB 显存以上的卡上。这样既能看到分层推理带来的内存优势又不至于被慢速折磨到怀疑人生。70B 模型可以跑但那更像是一种“我能跑起来”的证明日常使用效率很低。最后再分享一个实用小技巧AirLLM 首次加载非常耗时如果你只是临时做个测试建议保持 Python 进程存活连续多次调用 generate权重重复加载的损耗会显著降低。我通常会在 Notebook 里把模型加载放在一个单独的单元格里跑一次就不再改了。省下来的时间比改代码的时间多得多。

相关新闻

多Agent环境下Skill管理实战:从重复存储到单一事实来源

多Agent环境下Skill管理实战:从重复存储到单一事实来源

1. 多 Agent 环境下 Skill 管理的真实困境 1.1 一个 Skill 存三份,到底是怎么发生的 先说一个我亲身踩过的场景。去年我同时在跑三个 Agent 工作流:一个负责代码审查,一个负责文档生成,还有一个负责数据清洗。三个 Agent 各自有独…

2026/10/16 11:11:19 阅读更多 →
C语言核心概念一次讲透:指针、结构体与内存管理

C语言核心概念一次讲透:指针、结构体与内存管理

不是我说,现在网上讲C语言入门的教程,十个里面有八个都在讲语法糖和“Hello World”,但真正决定你能不能拿C语言干活的核心概念,反而被一笔带过。我当年带过不少新人,也看过太多人卡在差不多的位置上:变量赋…

2026/10/16 11:11:19 阅读更多 →
用AiPy做汇报PPT:五个高效步骤,从需求描述到成稿一键生成

用AiPy做汇报PPT:五个高效步骤,从需求描述到成稿一键生成

不用再对着空白PPT一页页拖文本框了。我用 AiPy 这个小工具做了几十份汇报材料,从需求描述到成品PPT,一分钟内出初稿,再花几分钟微调,比自己从零排版快出一个量级。这篇文章就把我跑通整个流程的 5 个高效步骤拆开讲,全…

2026/10/16 11:11:19 阅读更多 →

最新新闻

单VLM智能体驱动机械臂操作:设计与落地的完整拆解

单VLM智能体驱动机械臂操作:设计与落地的完整拆解

做机器人操作的人都有一个共同的感受:传统控制链路太长太脆。以前做一个“让机械臂把红色方块放到蓝色盒子里”的任务,你得串联目标检测、位姿估计、运动规划、抓取策略、异常处理,一路下来光是调参就能耗掉一周,换个工件颜色可能…

2026/10/16 20:26:55 阅读更多 →
MACOGA:面向动态栅格环境的鲁棒路径规划进化算法

MACOGA:面向动态栅格环境的鲁棒路径规划进化算法

1. 这不是又一个“改进GA”的噱头:TEVC 2026录用的MACOGA到底在解决什么真问题?你可能已经看过太多标题里带“改进”“优化”“新型”“自适应”的进化算法论文——点开一看,不过是把交叉算子换个名字、把变异概率加个sigmoid函数&#xff0c…

2026/10/16 20:26:55 阅读更多 →
Blender自动拓扑插件Quad Remesher:安装、参数与工作流指南

Blender自动拓扑插件Quad Remesher:安装、参数与工作流指南

简介:quad-remesher 1.23 自动拓扑工具面向 Blender 中高级建模用户,可针对雕刻高模、游戏资产与影视场景模型快速生成规整四边形布线,解决手工重拓扑耗时长、面数分布不均的问题,尤其适合角色、硬表面等复杂模型的快速重拓扑。压…

2026/10/16 20:26:55 阅读更多 →
Matlab2019b音频频谱分析:FFT与PSD实操指南

Matlab2019b音频频谱分析:FFT与PSD实操指南

1. 这不是教科书里的FFT:一个音频工程师日常怎么用Matlab2019b“听懂”信号在Matlab2019b里敲下fft(),不等于真正理解了音频信号。我带过三届某高校信号处理课程的实验课,也给某音频设备公司做过半年的算法验证支持,最常听到学生和…

2026/10/16 20:26:55 阅读更多 →
Doc2vec原理与实战:文档级语义向量建模技术详解

Doc2vec原理与实战:文档级语义向量建模技术详解

1. 这不是又一个“词向量”复读机:Doc2vec到底在解决什么真问题?你肯定见过这样的场景:某高校实验室里,A同学正对着几十篇PDF格式的课程论文发愁——导师要求他从这批材料里自动找出三类研究方向相近的论文群组,还要给…

2026/10/16 20:25:54 阅读更多 →
KillSwitch:专为对抗LLM而生的编程语言设计解析

KillSwitch:专为对抗LLM而生的编程语言设计解析

1. 这个项目到底在对抗什么第一次看到"KillSwitch – a programming language designed to be difficult for LLMs"这个标题时,我愣了一下。做编程语言的人通常追求的是清晰、简洁、容易上手,怎么会有反其道而行之,专门设计一门让A…

2026/10/16 20:25:54 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →