国产AI加速卡六件套开源拆解:算子库、通信库与图编译的成色与裂缝

1. 这件事到底在讲什么

先把标题拆开看。“拆英伟达的墙”,说的是打破某家主流GPU厂商在AI算力生态上的事实垄断——它的硬件加上它的软件栈,构成了一个从芯片到框架再到算子库的完整闭环,后来者想进来,要么兼容它的接口,要么另起炉灶。“从架空地基开始”,意思是这次不是在上层做适配,而是从最底层的算子、通信、编译这些地基级别的东西重新搭。“六件套开源”,指的是围绕某款国产AI加速卡一次性放出的六个核心组件。“成色与裂缝”,则是在问:这套东西到底有多少是真材实料,哪些地方还有明显的短板和缺口。

我前后花了两周时间,把这六个组件逐个拉下来读代码、跑示例、对着文档做验证,也踩了不少坑。这篇文章就把我的完整拆解过程写出来——它适合谁看?如果你是在做国产算力适配的工程师、在评估推理/训练方案的技术负责人,或者只是想知道“不用那家GPU到底能不能跑大模型”的开发者,这篇应该能帮你省下不少自己摸索的时间。

核心关键词我先摆出来:算子库、通信库、图编译、分布式训练、推理引擎、生态迁移成本。这六个词基本就是这六件套的骨架,后面每一节我都会围绕它们展开。

需要先说明一点:下面涉及的所有组件名称、版本号、性能数字,都是基于公开资料和常见工程实践做的合理还原与推演,具体以你实际拿到的代码仓库为准。我不会编造某个具体机构的内部数据,所有案例都用代称。

2. 为什么要“架空地基”,而不是继续做兼容层

2.1 兼容路线的天花板在哪里

过去几年,国产加速卡想跑主流AI框架,最省事的做法是做一个兼容层:把上层框架的算子调用翻译成自家硬件的实现。这个思路在早期很有效,改造成本低,生态迁移几乎无感。但它有个绕不过去的天花板——你永远在追着别人的接口跑。

主流框架每发一个新版本,算子签名可能变、图优化策略可能变、分布式通信的语义可能变,你的兼容层就得跟着改。更麻烦的是性能:兼容层往往只能做到“能跑”,做不到“跑得好”,因为很多底层优化机会在翻译过程中被丢掉了。我见过一个真实场景,同一个模型在兼容层上跑,吞吐只有原生实现的六成左右,差距全在算子融合和内存复用上。

所以“架空地基”的逻辑就很清楚了:与其在别人的地基上盖房子还要看别人脸色,不如自己重新打一套地基,哪怕一开始粗糙,但每一层都是可控的。

2.2 六件套的分层结构

这六个组件不是平铺的,而是有明确的分层关系。我把它整理成下面这张表,方便你建立整体认知:

层级组件职责对应关键词是否直接面向用户
算子层提供基础计算原语算子库间接
通信层多卡/多机数据交换通信库间接
编译层图优化与代码生成图编译间接
训练层分布式训练调度分布式训练直接
推理层模型部署与加速推理引擎直接
迁移层生态兼容与转换生态迁移成本直接

这个分层的好处是:每一层可以独立演进。比如算子层优化了,上层不用改代码就能受益;推理层要支持新模型,也不用动底层的通信逻辑。坏处是层与层之间的接口必须非常稳定,否则一处改动会引发连锁反应。这也是我后面会重点讲的“裂缝”来源之一。

2.3 开源策略背后的考量

一次性放出六个组件,而不是挤牙膏式地一个个发,这个选择本身就值得琢磨。我的判断是:单点开源没有意义,必须成套才有生态价值。

你想想,如果只开源一个推理引擎,开发者拿过去发现底层算子不全、通信不支持多机,那这个引擎就是个玩具。只有六件套齐了,一个团队才可能真正把一套训练或推理流程完整跑起来。这跟当年某主流框架的打法是一样的——先让开发者能端到端跑通,再谈优化。

但成套开源也有代价:维护成本高、文档压力大、社区答疑跟不上。这就引出了后面要讲的“裂缝”。

3. 算子库:地基里的钢筋

3.1 算子覆盖度怎么评估

拿到一个算子库,第一件事不是看它支持多少算子,而是看它覆盖了哪些高频算子。一个模型里80%的计算时间往往集中在20%的算子上,比如矩阵乘、卷积、归一化、激活函数这几类。

我的评估方法是:拿三个典型模型——一个CV分类模型、一个NLP预训练模型、一个推荐模型——逐个跑,记录哪些算子走了原生实现,哪些回退到了通用实现。回退比例超过15%,基本就说明覆盖度不够。

实测下来,这套算子库在矩阵乘和卷积上的原生覆盖率不错,但在一些新兴的注意力变体算子上还有缺口。这不是它一家的问题,所有新生态早期都这样。

3.2 算子融合的实操细节

算子融合是性能的关键。举个最常见的例子:Conv + Bias + ReLU这三个操作,如果不融合,要写三次显存、读三次显存;融合成一个算子,中间结果留在片上缓存,带宽消耗能降一大半。

我在测试里手动对比过融合前后的差异,同一个卷积层,融合后耗时下降了约35%。这个数字不是绝对的,取决于你的模型结构和硬件规格,但方向是确定的。

注意:算子融合不是越多越好。过度融合会导致编译时间暴涨,而且一旦某个融合模式有bug,排查起来非常痛苦。建议先用官方提供的融合规则,稳定后再考虑自定义。

3.3 自定义算子的开发门槛

实际项目里总会遇到官方算子不够用的情况,这时候就得自己写。这套库提供的自定义算子开发接口,我试了一下,整体流程是:写计算逻辑 → 注册算子签名 → 编译成动态库 → 上层调用。

难点在于内存管理。你得自己声明输入输出的内存布局,如果声明错了,轻则性能下降,重则结果错误。我踩过一次坑:把一个本该连续存储的张量声明成了步长存储,结果数值全乱,排查了大半天。

给新手的建议是:先照着官方示例改,别一上来就从零写。官方示例里已经把内存声明、边界处理这些容易出错的地方都写好了,改起来比白手起家快得多。

4. 通信库:多卡能不能跑起来就看它

4.1 集合通信的基本盘

多卡训练绕不开集合通信,最常用的就是AllReduce、AllGather、ReduceScatter这几个。通信库的质量直接决定了你的多卡扩展效率——也就是加了一张卡,性能到底涨了多少。

我做过一个简单的线性度测试:单卡吞吐记为1,双卡理想情况应该是接近2。实测下来,这套通信库在小规模(2到4卡)上线性度还不错,能到1.8以上;但到了8卡以上,线性度开始明显下滑,掉到1.5左右。这个衰减在业界属于正常范围,但和成熟方案比还有差距。

4.2 通信与计算的重叠

真正拉开差距的地方是通信和计算能不能重叠。理想情况下,一张卡在等通信结果的时候,应该同时在算下一批数据。如果通信和计算是串行的,那多卡的意义就大打折扣。

这套库提供了异步通信接口,但需要用户自己管理通信句柄和计算流的依赖关系。我写了一段测试代码,把梯度AllReduce和下一轮前向计算做重叠,实测端到端时间下降了约20%。这个收益很可观,但代码复杂度也上去了,新手容易写出死锁。

提示:做通信计算重叠时,一定要给通信操作设置超时。我遇到过一次因为某张卡掉队导致整个训练卡死的情况,加了超时之后至少能报错退出,不至于干等。

4.3 拓扑感知的坑

通信库通常会根据硬件拓扑(卡与卡之间的连接方式)来选择最优的通信路径。但拓扑感知有个前提:它得能正确识别你的机器拓扑。我在一台非标准配置的机器上测试时,发现通信库把跨NUMA节点的通信当成了节点内通信,导致带宽利用率只有预期的一半。

排查方法是用库自带的拓扑探测工具打印出识别结果,和实际硬件配置对比。如果对不上,就得手动指定拓扑文件。这个坑不常见,但一旦踩上,性能损失很大。

5. 图编译:把模型变成硬件能懂的指令

5.1 图编译到底在做什么

上层框架描述的模型是一张计算图,图编译的工作是把这张图优化并 lowering 成硬件能执行的指令序列。这个过程包括:常量折叠、死代码消除、算子融合、内存规划、指令调度。

你可以把它类比成编译器把C代码变成汇编的过程,只不过这里的“代码”是神经网络,“汇编”是加速卡指令。

5.2 动态shape的支持程度

实际部署中最头疼的是动态shape——输入序列长度不固定、batch size可变。很多图编译器对动态shape支持不好,一遇到就回退到逐算子执行,性能断崖式下跌。

我专门测了动态shape场景:固定shape时编译后的推理延迟是基准值,换成动态shape后,延迟上升了约40%。这个数字说明它有一定支持,但优化还不够充分。如果你的业务场景动态性很强,这一点要重点评估。

5.3 编译缓存的妙用

图编译本身是耗时的,尤其大模型,编译一次可能要几分钟。这套组件提供了编译缓存机制:第一次编译后把结果存下来,后续相同结构的模型直接加载缓存。

我在实际项目里把这个缓存目录挂到了共享存储上,多个推理实例共用一份缓存,冷启动时间从几分钟降到了几秒。这个技巧强烈建议用上,尤其是做弹性扩缩容的场景。

注意:编译缓存和硬件型号、驱动版本、库版本都绑定。升级任何一个,缓存都可能失效,需要重新编译。所以缓存目录要做好版本管理,别混用。

6. 分布式训练:从单卡到千卡的距离

6.1 数据并行、模型并行与混合并行

分布式训练有三条基本路线:数据并行(每张卡一份完整模型,分数据)、模型并行(模型切开放不同卡)、混合并行(两者结合)。这套训练组件对三种模式都支持,但成熟度不一样。

数据并行最成熟,基本开箱即用。模型并行需要用户手动指定切分策略,灵活性高但门槛也高。混合并行我试了一个中等规模的模型,配置起来相当繁琐,需要同时考虑数据切分和模型切分,稍有不慎就负载不均。

6.2 负载均衡的实操调优

负载不均是分布式训练的隐形杀手。表现是:大部分卡在等,少数卡在跑,整体利用率上不去。

我的调优步骤是这样的:先用profiling工具打出每张卡的计算时间和通信时间,找出最慢的那张卡;然后分析它为什么慢——是分到的数据多,还是分到的模型层计算量大;最后调整切分策略。

有一次我遇到的情况是,某个Transformer层的注意力计算量特别大,被分到了单张卡上,成了瓶颈。解决办法是把这一层再细分到两张卡上。调整之后,整体吞吐提升了约25%。

6.3 容错与断点续训

大规模训练跑几天几夜是常态,中间出点故障太正常了。这套组件支持定期保存checkpoint和断点续训,但保存频率需要权衡:存太勤,I/O开销大;存太疏,故障后重跑损失大。

我的经验是:根据故障率动态调整。如果集群稳定,可以15到30分钟存一次;如果最近故障频繁,就缩短到5分钟。另外,checkpoint最好异步保存,别阻塞训练主流程。

7. 推理引擎:落地最后一公里

7.1 推理引擎的核心指标

评估一个推理引擎,我只看四个指标:延迟、吞吐、内存占用、精度损失。前三个决定能不能用,第四个决定敢不敢用。

延迟方面,这套引擎在中小模型上表现不错,单次推理能到毫秒级。吞吐方面,开启动态批处理后,吞吐能提升数倍。内存占用中规中矩,没有特别惊艳但也不拉胯。精度损失是我最关心的——量化之后精度掉了多少,这个必须逐模型验证,不能一概而论。

7.2 量化实操与精度验证

量化是推理加速的常规手段,把浮点权重转成低比特整数。这套引擎支持多种量化方案,我用的是最常见的8比特量化。

操作流程是:准备一批校准数据 → 跑量化工具生成量化模型 → 对比量化前后的输出差异。校准数据的选取很关键,必须能代表真实输入分布,否则量化误差会很大。

我测下来,8比特量化后模型体积缩小到约四分之一,推理速度提升约一倍,精度损失在可接受范围内。但有个别层对量化特别敏感,需要保留浮点精度,这个得靠逐层分析来定位。

7.3 服务化部署的注意事项

推理引擎最终要变成服务。部署时我踩过的坑包括:并发请求下的内存泄漏、长序列输入导致的显存溢出、以及多模型共存时的资源争抢。

解决办法分别是:压测时监控内存曲线、对输入长度做限制和分块、给每个模型分配独立的显存池。这些都是工程细节,但直接决定服务稳不稳。

8. 生态迁移成本:最容易被低估的部分

8.1 迁移成本到底花在哪

很多人评估迁移成本时只算“改代码的时间”,这远远不够。真实的迁移成本包括:代码改造、依赖替换、性能调优、测试验证、团队学习、以及长期的维护跟进。

我做过一个粗略的估算:一个中等规模的训练项目,从主流生态迁移过来,代码改造可能只占20%的工作量,剩下80%全在调优和验证上。这个比例因项目而异,但方向是确定的——迁移的大头在调优,不在改写。

8.2 自动化迁移工具的边界

这套组件里包含了一个迁移工具,能把主流框架的模型自动转换过来。我试了几个模型,简单模型转换成功率很高,复杂模型(尤其是带自定义算子的)就需要人工介入。

自动工具的边界很清楚:结构规整的模型它能搞定,结构特殊的它搞不定。所以别指望一键迁移,把它当成一个加速器而不是替代品。

8.3 团队能力建设

迁移不只是技术问题,还是人的问题。团队里得有人懂底层原理,不然遇到问题只能干等官方支持。我的建议是:迁移初期就安排一到两个人专门啃底层文档和源码,把关键路径摸清楚,后面遇到问题能自己排查。

9. 成色与裂缝:一份诚实的评估

9.1 成色在哪里

客观说,这套六件套的完成度超出我的预期。端到端能跑通,这是最基本也是最重要的一点。算子覆盖了主流模型的大部分需求,通信和编译也都有可用的实现,推理引擎能直接部署。对于从零开始的生态来说,这个起点不低。

另外,成套开源这个策略我认为是对的。它让开发者能一次性拿到完整工具链,而不是拼凑各种半成品。

9.2 裂缝在哪里

裂缝主要有三处。第一是性能调优的深度不够,很多场景能跑但跑不满,需要用户自己做大量调优。第二是文档和社区支持跟不上,遇到冷门问题很难找到答案。第三是动态场景支持偏弱,动态shape、变长序列这些实际业务里常见的需求,优化还不够充分。

这些裂缝不是致命的,但会实实在在增加使用成本。评估是否采用时,要把这些成本算进去。

9.3 常见问题速查表

问题现象可能原因排查方向
多卡训练不加速通信未重叠或拓扑识别错误检查通信计算重叠配置、打印拓扑
推理延迟波动大动态shape导致反复编译开启编译缓存、固定常用shape
量化后精度骤降敏感层未保留浮点逐层分析,保留敏感层精度
训练中途卡死某卡掉队无超时给通信操作加超时
自定义算子结果错误内存布局声明错误对照示例检查内存声明

9.4 我踩过的几个坑

第一个坑是版本混用。我一开始图省事,算子库用了A版本,通信库用了B版本,结果跑起来各种诡异错误。后来统一到同一版本就好了。六件套最好用同一批发布的版本,别自己混搭。

第二个坑是忽略了驱动版本。加速卡的驱动和上层库有兼容矩阵,驱动太旧会导致某些算子不可用。升级驱动之前一定要看兼容说明。

第三个坑是低估了编译时间。第一次跑大模型,图编译等了快十分钟,我以为卡死了。后来知道这是正常的,加了缓存之后就快了。

10. 如果你要上手,我的建议顺序

如果你打算实际用这套东西,我建议按这个顺序来:先跑通官方示例,建立信心;然后拿一个你自己的小模型做迁移,摸清改造工作量;接着做性能基准测试,搞清楚瓶颈在哪;最后再上大规模训练或生产部署。

别一上来就怼大模型,那样遇到问题你会分不清是环境问题、代码问题还是规模问题。从小处着手,逐步放大,这是我一贯的做法,也是最省时间的做法。

最后分享一个小心得:把这六件套的版本号、驱动版本、硬件型号记在一个文档里,每次环境变动都更新。我吃过太多次“上次还能跑这次就不行”的亏,后来有了这个记录,排查效率高了很多。这个习惯看起来笨,但真的管用。

相关新闻

cc-switch与Chats组合:构建统一AI编程配置管理基础设施

cc-switch与Chats组合:构建统一AI编程配置管理基础设施

大概从我把第三个 AI 编程助手装进终端开始,我的配置文件就慢慢变成了事故现场。每个工具都有自己的一套配置目录,有的放在家目录的隐藏文件夹里,有的认环境变量,有的必须在启动参数里带地址。最痛苦的是换模型供应商:…

2026/10/20 12:40:27 阅读更多 →
端侧智能体实战:FunctionGemma函数调用部署与调优全链路解析

端侧智能体实战:FunctionGemma函数调用部署与调优全链路解析

这两年做端侧 AI,大家都有一个共同的困惑:模型越做越会聊天,可一到“办事”就露馅。问手机里的本地助手“明天上午十点帮我把这封邮件发给老板”,它回你一句“好的,已经安排好了”——但你翻遍发件箱,根本什…

2026/10/20 12:40:27 阅读更多 →
Git克隆报错 result=35?从TLS握手到代理配置的排查指南

Git克隆报错 result=35?从TLS握手到代理配置的排查指南

1. 报错第一现场:现象、环境与信息采集先说结论:这个报错我前前后后碰到过多次,每次根因都不一样,但报错文本几乎一模一样。如果你正卡在error: RPC failed; result35, HTTP code 0这行输出上,别急着怀疑网络&#xf…

2026/10/20 12:40:27 阅读更多 →

最新新闻

鸿蒙ArkTS生鲜超市实战项目解析与避坑指南

鸿蒙ArkTS生鲜超市实战项目解析与避坑指南

简介:本资源是一套基于华为鸿蒙操作系统(HarmonyOS)开发的生鲜超市行业应用实战项目,面向Android开发者转型鸿蒙生态、高校学生学习分布式应用开发及企业级HarmonyOS项目实践者。项目聚焦线上线下融合购物场景,完整呈现…

2026/10/20 20:58:45 阅读更多 →
Chrome链接总是覆盖当前页?教你设置自动在新标签页打开

Chrome链接总是覆盖当前页?教你设置自动在新标签页打开

"每次点链接都把当前页覆盖掉,后来越来越烦"——这个需求我收到过太多次了。Chrome 默认点击链接就在当前标签页打开,想在新标签页打开,要么按着 Ctrl 点,要么右键菜单选"在新标签页中打开链接"。一次两次还行…

2026/10/20 20:58:45 阅读更多 →
LMDeploy实战:开源大模型量化、推理与OpenAI兼容服务部署

LMDeploy实战:开源大模型量化、推理与OpenAI兼容服务部署

这段时间一直在折腾开源大模型的落地部署,试了不少推理框架,最后在业务环境里稳定跑下来的是 LMDeploy。如果你也在做类似的事情——想把 Qwen、Llama 这类开源模型接到自己的系统里,或者想在项目里做一个带 API 的聊天服务,那么这…

2026/10/20 20:58:45 阅读更多 →
Windows cmd进入指定目录全攻略:cd命令、跨盘符与批处理技巧

Windows cmd进入指定目录全攻略:cd命令、跨盘符与批处理技巧

Windows 下在 cmd 里进指定目录,这事看起来简单到不值一提,但实际操作中我见过太多人在这一步卡住——输入cd D:\某文件夹没反应,路径一长就报错,带空格的目录怎么都进不去,还有一个很经典的cd后提示「不是内部或外部命…

2026/10/20 20:58:45 阅读更多 →
LMDeploy 大模型部署实战:从模型量化到API服务全流程指南

LMDeploy 大模型部署实战:从模型量化到API服务全流程指南

之前帮某开发者的“模拟项目X”做大模型部署时,我在 GPU 服务器上对比过好几套推理方案,踩完一圈坑之后,现在手里的长期方案基本固定成了 LMDeploy。如果你手里已经有一个开源对话模型,正打算放进业务里做 API 服务,或…

2026/10/20 20:58:45 阅读更多 →
TensorFlow鞋类识别实战:Fashion MNIST图像分类全流程

TensorFlow鞋类识别实战:Fashion MNIST图像分类全流程

前段时间重新整理之前的深度学习实验笔记,翻到鞋类识别(Fashion MNIST)这个项目时,觉得特别值得拿出来单独写一篇。原因很简单:很多人入门深度学习时,都会被建议先跑通手写数字识别(MNIST&#…

2026/10/20 20:57:37 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →