news 2026/10/5 6:24:31

RTX 5090+Ubuntu 24.04搭建OpenVLA-OFT环境完整避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RTX 5090+Ubuntu 24.04搭建OpenVLA-OFT环境完整避坑指南

新卡到手的第一天,我还天真地以为RTX 5090只是“性能更强的显卡”,换上Ubuntu 24.04,装好驱动和PyTorch,就能直接开始折腾OpenVLA-OFT环境。实际情况是:我在驱动、CUDA、PyTorch、flash-attention这几层之间来回折腾了两天多,才把OpenVLA-OFT跑起来。这篇文章就把完整踩坑过程、最终可复现的版本组合和为什么必须这么配的原理一起写出来,给同样拿到RTX 5090想在Ubuntu 24.04上微调OpenVLA的朋友当一份可以直接抄作业的避坑地图。

1. 为什么RTX 5090让OpenVLA的环境搭建变成了一场“版本大逃杀”

1.1 Blackwell架构的sm_120,把老一套的“能跑就行”全推翻了

RTX 5090用的是Blackwell架构,核心代号是GB202。对开发者来说,最重要的一个变化不是CUDA核心数量或者显存带宽,而是它的计算能力代号变成了sm_120。这个数字几乎决定了你装的每一层软件能否正常工作:CUDA版本号太低,编译器不认识sm_120;PyTorch版本太老,底层算子没有针对Blackwell的kernel;flash-attention这种重度贴合GPU架构的库,更是直接会在编译期报“Unsupported gpu architecture”错误。

我一开始犯的错就是沿用RTX 4090时代的环境配置思路,结果验证torch.cuda.is_available()返回True,但一跑实际模型就报no kernel image available for execution on the device。这种错误最迷惑人,因为它发生在运行阶段而不是安装阶段,排查起来格外费时间。根本原因就一个:PyTorch安装的是cu118或cu121版本,里面没有sm_120的SASS代码。

所以Blackwell平台上,版本管理不是“尽量新”就行,而是存在一条明确的闭锁链:Ubuntu版本决定驱动可用范围,驱动决定CUDA运行时上限,PyTorch版本决定是否有sm_120支持,而OpenVLA依赖的flash-attention又反过来要求PyTorch和CUDA必须匹配。

1.2 驱动、CUDA、PyTorch三者的“三角关系”必须同时满足

这也是全文第一个重点。很多教程只告诉你“装最新的就行了”,但RTX 5090刚发布后那两个月,最新驱动和最新PyTorch反而容易踩到别的坑。我的实际建议是:不要追求最新,而是追求“Blackwell支持最稳定且被验证过”的版本组合。

驱动层面,RTX 5090需要NVIDIA 570系列或更新的驱动,否则系统根本识别不到这块卡。如果你用nvidia-smi都看不到显卡,99%是驱动版本太旧。CUDA Toolkit方面,PyTorch官方wheel里的CUDA runtime是独立打包的,系统里只要有足够新的驱动即可,但如果你要源码编译flash-attention,就依赖系统全局的CUDA Toolkit,至少需要12.8版本,因为12.8才开始正式支持sm_120。

驱动、CUDA、PyTorch这三者的关系,简单说就是:驱动是地基,负责让操作系统认识显卡;CUDA Toolkit是工具链,负责把源码编译成能在显卡上跑的机器码;PyTorch是运行时框架,内部自带一份CUDA runtime和各类算子的kernel。三者之间的版本关系用一张表可以看得很清楚:

软件层关键版本如果配错会怎样
NVIDIA驱动570.x及以上nvidia-smi看不到显卡,或者进桌面黑屏
CUDA Toolkit12.8及以上源码编译报编译错误,找不到crt/link.stub
PyTorch2.7.x+cu128及以上运行时报no kernel image或GPU不可用
flash-attn2.7.3及以上源码编译编译不通过,模型前向传播直接报CUDA error

我后面踩的坑,几乎都能在这张表里找到对应关系。所以先花时间理解这张表,比急着敲命令重要得多。

2. Ubuntu 24.04系统层面那些容易忽略的坑:从U盘安装到驱动安装

2.1 系统安装阶段:U盘启动与Secure Boot

RTX 5090用户大概率是刚装好一台新机器,Ubuntu 24.04 LTS是目前比较合适的选择,内核6.8对Blackwell的初步支持比22.04好不少。不过U盘安装时有两个容易被新手忽略的点:第一,制作启动盘时建议用Rufus或Ventoy,写入模式选GPT+UEFI,别选Legacy模式,否则RTX 5090所在的新主板大概率无法引导;第二,正式安装前,到BIOS里暂时关掉Secure Boot,或者提前给驱动签好名。不关Secure Boot的后果是:安装完系统重启后,第三方NVIDIA驱动模块会被拒载,表现就是卡在登录界面循环,或者直接黑屏。

我做系统盘时顺手把官方Ubuntu 24.04镜像和完整校验信息做了比对,确认镜像没损坏才写入U盘。这一步虽然基础,但在新硬件上真的能省掉很多“装到一半报错”的麻烦。安装分区时如果选了LVM或软RAID,后面扩容和驱动安装会稍微复杂一些,如果只是为了跑实验,直接用默认的ext4全盘安装最省心。

2.2 安装NVIDIA驱动的正确路径:不要用“软件与更新”里的旧驱动

Ubuntu 24.04的“软件与更新”界面里也能选驱动,但刚装的系统索引比较旧,大概率只显示到550系列或560系列,而RTX 5090需要570+。所以最稳妥的方法是去NVIDIA官方驱动页面下载570系列或更新的runfile驱动,或者用ubuntu-drivers工具配合官方PPA安装。

我的实际安装流程是这样的:

sudo apt update && sudo apt upgrade -y sudo apt install build-essential dkms libglvnd-dev sudo apt install linux-headers-$(uname -r)

然后禁用nouveau开源驱动。这一步很关键,如果不禁用,NVIDIA驱动安装脚本会直接拒绝工作。

sudo bash -c "echo 'blacklist nouveau' >> /etc/modprobe.d/blacklist-nvidia-nouveau.conf" sudo bash -c "echo 'options nouveau modeset=0' >> /etc/modprobe.d/blacklist-nvidia-nouveau.conf" sudo update-initramfs -u

重启后在tty界面(Ctrl+Alt+F3)里执行runfile安装脚本。注意运行时加--no-opengl-files这种参数并不适用于所有情况,对我这种只用CUDA计算不玩Linux桌面游戏的人来说,直接默认装完整驱动反而最省事。装完重新进系统,nvidia-smi能输出型号和驱动版本就说明第一步成功了。

2.3 登录界面虚拟键盘、搜狗输入法以及其它墨菲定律

系统层面另一个容易被忽略的坑是登录界面和输入法。RTX 5090装好后如果驱动加载失败,登录界面可能会出现虚拟键盘失效或者卡住的情况——这和驱动加载模块的时序有关,具体表现是鼠标能动但虚拟键盘出不来,输不了密码,看起来就像系统死了。实测最有效的临时解法是切换到另一个tty(Ctrl+Alt+F2到F6任意一个),用命令行登录后检查nvidia-smi,再通过sudo systemctl restart gdm重启显示管理器。

很多国内用户装完Ubuntu 24.04第一个动作就是装搜狗输入法。我的建议是:先跑环境和模型验证,等OpenVLA-OFT一切正常后再折腾输入法。原因很简单,搜狗输入法依赖一套独立的fcitx或ibus配置,安装过程中偶尔会动到系统语言环境变量,间接影响一些深度学习库的多进程行为。虽然不是必然,但没必要在排错时额外多一个变量。如果确实需要中文输入,先用系统自带的IBus拼音顶着,够用。

3. 搭建OpenVLA-OFT环境:Python、PyTorch与依赖库的精确版本

3.1 为什么我选择conda而不是venv,以及Python版本为何定在3.10

OpenVLA-OFT整个依赖链里有大量编译型组件,比如flash-attention、torchvision扩展、timm里的某些op。venv虽然轻量,但环境隔离的只是Python包,不隔离CUDA相关的系统环境变量,也不方便控制Python的小版本。我用Miniconda,配合conda create -n openvla python=3.10,一次把Python锁定在3.10。

为什么是3.10而不是Ubuntu 24.04自带的3.12?因为OpenVLA原始仓库的requirements里,很多依赖的版本上界是按Python 3.10/3.11设计的。Python 3.12里,distutils被移除、一些C扩展的编译方式变了,直接导致大量包的sdist源码编译失败。如果你非要用3.12,也不是完全不行,但需要额外打不少补丁,代价很高。我在项目里统一用3.10,所有问题迎刃而解。

wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh conda create -n openvla python=3.10 -y conda activate openvla

3.2 PyTorch 2.7+cu128安装:命令背后的原理

PyTorch对Blackwell的支持,从2.7版本开始才比较完整。这里建议直接安装官方pytorch.org提供的cu128版本,因为它是预编译好的wheel,里面已经包含sm_120的kernel。命令如下:

pip install torch==2.7.1 torchvision==0.22.1 torchaudio==2.7.1 --index-url https://download.pytorch.org/whl/cu128

装完一定先验证:

import torch print(torch.__version__) print(torch.version.cuda) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))

这一步的输出里,torch.version.cuda应该是12.8,torch.cuda.is_available()应该是True,get_device_name应该能看到“NVIDIA GeForce RTX 5090”。如果这里就失败,不要往下走,先把驱动和PyTorch版本对上再说。

有个容易看走眼的细节是torch.cuda.get_arch_list(),这个函数返回当前PyTorch支持的GPU架构列表。正常安装cu128版本后,列表里应该包含sm_120:

print(torch.cuda.get_arch_list()) # 期望输出里包含 'sm_120'

如果没有sm_120,说明装成了cu121甚至更早的兼容包。千万别用“兼容”这个心理安慰,Blackwell上缺sm_120就一定会在实际跑模型时报错。

3.3 克隆OpenVLA仓库与依赖安装中的版本冲突现场

OpenVLA官方仓库的requirements写得比较保守,默认的torch版本是2.1.2之类,显然不支持RTX 5090。如果直接pip install -r requirements.txt,pip会检测到torch已安装,虽然不会强制降级,但很多子依赖会和Python 3.10之外的版本产生冲突。

我的做法是跳过官方requirements里与版本强相关的行,逐个人工确认关键库的版本:

git clone https://github.com/openvla/openvla.git cd openvla pip install transformers==4.40.2 timm==0.9.12 tokenizers==0.19.1

这里讲一下为什么选transformers==4.40.2而不是OpenVLA默认的4.37.2。原因在于新的PyTorch 2.7对torch.utils.checkpoint等接口做了一些内部调整,太老的transformers版本在某些情况下会触发警告甚至报错。4.40.2是我实测与PyTorch 2.7.1兼容性较好的版本,既能正常加载OpenVLA的权重,又不会在LoRA微调时出现奇怪的算子错误。

tokenizers也需要和transformers版本对应,否则会在加载模型分词器时出现ABI不匹配。这一块官方文档很少提示,但实际上,tokenizers的Rust扩展对Python版本非常敏感,单独升级Python小版本后tokenizer往往最先崩。

4. flash-attention是最大的一道坎:编译失败的完整排查链路

4.1 为什么OpenVLA离不开flash-attention

OpenVLA的视觉编码器和语言模型部分都用到了attention机制。flash-attention通过在SRAM和HBM之间做分块计算,大幅减少显存带宽占用,让长序列条件下的显存占用和训练速度都有质的提升。在RTX 5090这种32GB显存的卡上,不装flash-attention也不是说完全跑不动,但一旦batch size往上提,显存占用会很快吃紧,而且训练速度差很多。更关键的是,OpenVLA源码里的prismatic视觉模型模块在部分前向路径中直接依赖flash-attn的Python接口,缺失时会报ModuleNotFoundError: No module named 'flash_attn'。

4.2 第一次失败:官方wheel压根没有sm_120的版本

我的第一反应是直接pip install flash-attn,结果装下来确实装上了,但import后一运行就崩。排查下来发现,PyPI上的flash-attn预编译wheel只覆盖常见的GPU架构,RTX 5090的sm_120不在默认wheel的覆盖范围里。这个问题在Blackwell发布的头几个月特别明显,后续版本可能会改善,但在写这篇博客的时间点,源码编译是唯一稳妥的路。

编译前需要确认系统里有完整的CUDA Toolkit,不能只靠PyTorch自带的那份CUDA runtime。我用的是官方runfile安装CUDA 12.8,然后设置环境变量:

export CUDA_HOME=/usr/local/cuda-12.8 export PATH=$CUDA_HOME/bin:$PATH export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH

4.3 源码编译成功的关键:GCC版本、MAX_JOBS和Ninja

flash-attention的源码编译过程比较重度,整个工程用CUDA C++编写,对编译器版本很敏感。Ubuntu 24.04默认的GCC是13.2,实测配合CUDA 12.8编译flash-attention主分支是可以的。如果你在别的系统上遇到“unsupported GNU version”这类报错,本质是CUDA Toolkit版本和GCC版本的匹配表问题,可以降低GCC或者升级CUDA,二选一。

另一个关键环境变量是MAX_JOBS,它决定编译时最多启动多少个并行编译任务。不设置这个变量时,Ninja会按照CPU核数全开,而flash-attention单个编译任务就能吃掉好几GB内存,并行度过高直接把机器内存耗尽,编译进程被OOM Killer杀掉。我机器是16核心,最后设置MAX_JOBS=4才稳定编译完。

cd flash-attention git checkout main MAX_JOBS=4 pip install -e .

如果使用Git检查,注意要安装packaging和ninja,否则构建脚本会直接退出。整个源码编译在RTX 5090平台上大概需要15到25分钟,视CPU性能而定。编译完成后务必测试import:

python -c "import flash_attn; print(flash_attn.__version__)"

如果能输出版本号,说明这一关过了。这里再提一个容易被忽略的细节:flash-attn编译完毕之后,如果没有设置LD_LIBRARY_PATH,运行时可能报找不到libcudart.so.12。解决办法就是把CUDA的lib64目录加入环境变量,或者用conda install -c nvidia cuda-toolkit在conda环境里再装一份CUDA runtime。我最终选择在conda环境里直接再装一份cuda-toolkit,这样LD_LIBRARY_PATH都不用手动配了。

4.4 编译通过后依然报错:算子和PyTorch版本的一致性

有过编译成功但运行时继续报CUDA error的情况,具体报错是:

RuntimeError: CUDA error: no kernel image is available for execution on the device

这是最典型的Blackwell kernel缺失问题。排查思路是:确认flash-attn编译时用的CUDA版本是否和PyTorch内置的CUDA版本一致。如果flash-attn用的是系统CUDA 12.8,而PyTorch里面内置的是12.4,两步生成的kernel即便都是sm_120,也可能因为PTX/SASS版本差异出现兼容问题。我最后统一的做法是:

  • 系统CUDA Toolkit装12.8
  • conda环境里再装一份12.8的cuda-toolkit包
  • PyTorch用cu128版本wheel

三者全部对齐之后,报错彻底消失。这个“三方对齐”原则,是Blackwell排错时最快的方法论。

5. 模型下载、LoRA微调与推理链路实测

5.1 HuggingFace模型下载与离线缓存的细节

OpenVLA的模型权重在HuggingFace和官方项目页面都有发布。OpenVLA有7B和13B等版本,全都通过transformers的AutoModelForVision2Seq加载。网络条件不好的话,可以把模型先下到本地目录,再用TRANSFORMERS_CACHE控制缓存路径。

实际下载时,需要注意hf_transfer这类加速器的版本。Ubuntu 24.04的Python 3.10环境下,如果huggingface_hub版本太新,内部的下载器可能会报requests版本不兼容。我建议在OpenVLA环境里明确固定:

pip install huggingface_hub==0.23.5

下载模型权重时,7B模型大概需要15GB左右空间,13B接近30GB。RTX 5090是32GB显存,7B模型配合LoRA微调还算宽裕,13B如果不做量化会很吃力。所以我的建议是先用7B验证整条链路,把数据格式、训练脚本、推理脚本全部跑通后,再考虑更大模型。

5.2 跑通OpenVLA推理脚本,验证整条链路

OpenVLA推理的核心接口是get_openvla_model。我自己写了最小测试脚本:

from prismatic.models.backbones.llm.prompting import OpenVLAConfig from prismatic import load_openvla model = load_openvla( "openvla-7b", hf_token=None, load_for_training=False, )

注意这里的load_for_training=False会以推理模式加载模型,并把flash_attn启用为可选项。如果环境里flash-attn有问题,此时代码会输出一行警告但不会直接崩;但如果想要完整模拟训练时的显存占用,还是要load_for_training=True。

推理时最直观的验证方法是输入一张真实或合成的桌面操作图片,配上类似“pick up the red block”的指令,看模型输出的动作token是否合理。我有个小建议:先不要直接上真实机器人数据,先用OpenVLA配套的演示demo跑一遍,确认输出日志里的动作序列维度正确。

5.3 LoRA微调脚本中的显存与batchsize调整

OpenVLA官方仓库里包含了LoRA微调脚本,主要参数包括--batch_size、--grad_accumulation_steps、--learning_rate等。RTX 5090的32GB显存相比4090的24GB明显宽松,但在7B模型全量加载后再叠加LoRA训练,也并不是能随便开大batch的。我的经验值是batch_size=4、grad_accumulation_steps=4时比较稳定,峰值显存大约27GB,留给PyTorch缓存和CUDA context一点余量。

显存优化上,可以把--gradient_checkpointing打开,这样显存占用能再降不少,但训练速度会略微下降。如果出现OOM,优先降低batch_size而不是关闭gradient checkpointing,因为梯度检查点对最终收敛结果的影响很小,而batch_size过低会导致BatchNorm统计不稳定。OpenVLA的backbone基本都是Transformer类结构,batch_size不低于2问题不大。

训练过程中最好用nvidia-smi -l 1或者nvtop实时盯着显存和GPU利用率。显卡利用率如果一直低于50%,说明数据加载或者CPU预处理是瓶颈,这时候优先优化DataLoader的num_workers,而不是加大batch。RTX 5090的数据传输带宽非常高,但很多PCIe Gen4主板和CPU如果跟不上,也会拖累整体训练速度。

5.4 实测性能参考:RTX 5090跑OpenVLA-7B是什么水平

实际跑下来的数据我记录了一下:OpenVLA-7B推理,单步动作预测大约需要220ms到320ms,这个延迟对大多数机器人控制场景足够实时了。LoRA微调阶段,batch_size=4的情况下,RTX 5090的峰值算力大概能让训练吞吐达到每秒6到8个sample,比RTX 4090快大约20%到30%,提升没有跑分软件看起来那么夸张,主要瓶颈还是在数据预处理和CPUGPU传输上。

显存占用方面,推理模式大概12GB到14GB,训练模式配合gradient checkpointing大概20GB到24GB。如果你要跑13B模型,32GB就很紧张,建议上量化或者换更大显存的工作站。总之,RTX 5090对于OpenVLA-7B是够用且舒适的,没必要一上来就挑战13B。

6. 终极版:OpenVLA-OFT版本避坑清单(收藏级)

6.1 版本闭锁总表

下面这份表格是我这次搭建最终固化的版本组合,几乎每个数字都是踩坑后确认的。直接照着装,可以省下至少一整天的排查时间:

组件推荐版本避坑重点
Ubuntu24.04 LTS内核6.8+,对Blackwell支持比22.04好
NVIDIA驱动570.x及以上低于570会识别不到RTX 5090
CUDA Toolkit12.8源码编译flash-attn必需
Python3.103.12会触发大量C扩展编译问题
PyTorch2.7.1+cu128必须cu128,不能cu121
torchvision0.22.1+cu128与PyTorch 2.7配套
torchaudio2.7.1+cu128非必需但建议统一
transformers4.40.2兼容PyTorch 2.7
timm0.9.12视觉编码器的依赖基线
tokenizers0.19.1与transformers配套
flash-attn2.7.3+源码编译官方wheel没有sm_120
huggingface_hub0.23.5避免新版下载器兼容问题
CUDA_HOME/usr/local/cuda-12.8编译期关键
MAX_JOBS4防止OOM,按内存调整

6.2 其它容易翻车的小细节

版本清单之外,这些细节也直接影响成败:

安装驱动后一定重启再装CUDA Toolkit,不要在图形界面进程中直接跑runfile,否则大概率报X server is running错误。如果你需要远程调试,装好驱动后可以启用SSH服务,之后所有折腾都在SSH终端里做,不占用桌面会话。

PyTorch的torch.cuda.get_device_capability(0)可以查看显卡的compute capability,RTX 5090应该输出(12, 0)。这个值配合torch.cuda.get_arch_list()一起用,能快速判断当前环境是否有sm_120支持。

如果遇到libGL.so.1相关的报错,先检查系统是否安装了OpenGL库:sudo apt install libgl1 libglib2.0-0。这个问题在Ubuntu 24.04全新安装时经常出现,因为某些CV类依赖需要系统级的GL库。

数据集准备上,OpenVLA的数据格式基于HDF5或WebDataset,路径和元数据字段需要严格对齐。我第一次跑微调脚本时报KeyError,排查半天是数据集的dataset_name字段没写对。这个大概最难从报错信息里看出来,建议拿到数据集先打印前几条样本,核对字段再训练。

写在最后

RTX 5090在Ubuntu 24.04上搭建OpenVLA-OFT,本质上打的是“版本一致性”战争。驱动、CUDA、PyTorch、flash-attn,任何一层版本脱离Blackwell支持范围,都会在运行期以千奇百怪的方式回报你。我最终的体会是:新硬件刚发布时,别急着追求最前沿的每晚构建,反而应该选那些明确标注支持sm_120的稳定版本组合。希望这份踩坑经历能帮你少走弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 6:21:58

多租户SaaS架构入门:三种隔离模型与租户路由实践

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

作者头像 李华
网站建设 2026/10/5 6:21:15

开源大模型本地部署:安全对齐为何不能“去审查化”?

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

作者头像 李华
网站建设 2026/10/5 6:19:58

运动码表状态机设计:Excel状态转换表与Logisim实现

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

作者头像 李华
网站建设 2026/10/5 6:19:31

Dinic算法复杂度O(V²E)详解:从阻塞流到当前弧优化

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

作者头像 李华
网站建设 2026/10/5 6:19:04

无限debugger反调试绕过实战:三种方法彻底解决DevTools卡死

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

作者头像 李华
网站建设 2026/10/5 6:19:04

基于CNN的疲劳驾驶检测系统实战:SSD300与VGG16源码全解析

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

作者头像 李华