LLM代理驱动GPU算子优化:从手写汇编到自动化搜索的工程实践
1. 从“手写汇编”到“代理驱动”算子优化范式正在换挡2026年过半如果你还在用纯手工的方式去调一个LLM推理里的Attention kernel大概率会被团队里那个“养了一堆代理”的同事按在地上摩擦。这不是危言耸听。过去两年GPU算子优化领域发生了一次静默但彻底的权力转移LLM代理LLM Agent从“辅助写代码的玩具”变成了“一等优化手段”。所谓一等意思是它不再是你在IDE里补全几行CUDA的插件而是直接参与搜索空间定义、编译参数决策、甚至运行时调度策略生成的核心角色。我所在的团队从2025年底开始把代理引入到算子优化流水线中覆盖了从FlashAttention变体、RMSNorm融合、到MoE路由算子的全链路。实测下来一个中等复杂度的融合算子纯手工调优到接近硬件峰值需要3到5天而代理驱动的工作流可以把首次可用版本压缩到4小时以内后续精调也只需要半天。这不是因为代理比人聪明而是因为算子优化的本质是一个高维、离散、带噪声的搜索问题而代理恰好擅长在人类给定的先验约束下做大规模并行试探。这篇文章不打算讲“代理有多神奇”这种空话。我会把过去半年踩过的坑、跑通的流程、以及那些“看起来能行但实际会炸”的细节全部摊开。目标读者是那些已经在写CUDA/Triton、对GPU架构有基本认知、但还没系统性地把代理纳入优化回路的工程师。如果你还在犹豫要不要入局我的建议是先跑通一个最小闭环再决定要不要深入。因为一旦跑通你很难回去。2. 为什么代理能成为一等优化手段三个不可逆的推力2.1 搜索空间爆炸与人类直觉的失效先看一个具体数字。一个典型的LLM解码阶段的融合算子涉及的可调参数包括block sizeM/N/K三个维度、warp数量、shared memory分块策略、流水线级数、寄存器分配上限、是否使用异步拷贝、是否启用Tensor Core特定指令、以及循环展开因子。这些参数组合起来合法配置轻松超过10^6种。人类工程师靠直觉和经验通常只能覆盖其中几十到几百种而且往往集中在“历史上见过能work”的区域。代理不一样。你给它一个参数空间定义和一个快速评估函数它可以在几小时内试探上万种组合。更关键的是它不会因为“上次这个配置炸了”就产生心理阴影也不会因为“导师说这样写才对”就放弃探索。它只认反馈信号。这就把算子优化从“经验驱动的艺术”拉向了“反馈驱动的工程”。但这里有个前提评估函数必须足够快。如果你每跑一个配置都要完整编译运行验证数值正确性那代理再强也跑不动。所以代理成为一等手段的第一个推力其实是轻量级评估基础设施的成熟。我们后面会详细讲怎么搭这个评估回路。2.2 代理的“代码生成自我修正”闭环已经足够可靠2024年的时候让LLM写一个能编译通过的Triton kernel还是碰运气。到了2026年情况完全变了。现在的代理可以做到给定一个算子语义描述和一组约束生成候选实现自动编译捕获编译错误根据错误信息修正代码再编译直到通过。这个闭环的可靠性在中等复杂度算子上已经超过90%。我实测过一个RMSNorm残差连接的融合算子代理在第三次尝试就生成了能通过数值验证的Triton实现而人类工程师写第一版通常也要改两三次。代理的优势不在于一次写对而在于它不厌其烦。人类改三次可能就烦了代理可以改三十次而且每次都会认真读错误信息。2.3 硬件迭代速度倒逼优化流程自动化2026年的GPU迭代节奏比三年前快了一倍。新架构出来旧的优化经验可能一半失效。以前一个资深工程师花两周摸清新架构的特性现在可能两周后下一代架构的文档就出来了。代理驱动的工作流可以把“摸特性”这个过程部分自动化你给它一个新架构的指令集文档和几个微基准测试它可以自动生成一批探测性kernel跑出实际吞吐和延迟数据然后归纳出这个架构的“脾气”。这不是说人类工程师没用了而是说人类的角色从“手写优化”变成了“设计搜索空间和评估函数”。你不再需要记住每个架构的shared memory bank冲突规则但你需要知道怎么定义一个合理的参数空间怎么设计一个不会被代理“钻空子”的评估函数。3. 代理驱动算子优化的最小闭环从零搭一套能跑的系统3.1 整体架构四个模块缺一不可一个能实际工作的代理驱动优化系统至少包含四个模块任务描述模块、候选生成模块、快速评估模块、搜索策略模块。这四个模块的接口设计比每个模块内部的实现更重要。我见过太多团队把精力花在“用哪个LLM”上结果接口一塌糊涂代理生成的代码根本没法自动评估整个流程卡死。任务描述模块负责把算子语义、输入输出形状、数据类型、目标硬件、性能目标延迟优先还是吞吐优先结构化。候选生成模块调用LLM生成代码或配置。快速评估模块负责编译、运行、验证数值正确性、采集性能数据。搜索策略模块决定下一步试探哪个配置。这四个模块之间必须通过结构化数据通信不能靠自然语言。比如候选生成模块输出的必须是一个包含代码字符串和配置字典的JSON对象而不是一段“我建议你试试block size 128”的文字。这一点极其关键因为自然语言接口在自动化回路里会引入不可控的解析错误。3.2 快速评估模块整个系统的性能瓶颈评估模块的设计直接决定整个系统的迭代速度。我们的做法是分两级第一级是编译静态检查只验证代码能否编译、资源占用是否超限寄存器、shared memory不实际运行。这一级可以在秒级完成。第二级是实际运行数值验证性能采集这一级通常需要几十秒到几分钟。为了加速我们把第二级做成异步队列代理可以连续提交多个候选评估模块并行跑。但这里有个坑GPU资源是有限的如果你同时跑太多候选它们会互相干扰性能数据。我们的经验是评估队列的并发度不要超过GPU数量的1.5倍否则采集到的延迟数据噪声会大到无法用于搜索决策。另一个坑是数值验证的容差设置。太松了代理会生成“看起来对但实际错”的kernel太紧了浮点误差会导致大量误报。我们的做法是对每个算子单独标定容差通常用相对误差1e-3到1e-4之间具体取决于算子的数值稳定性。3.3 搜索策略不要一上来就用贝叶斯优化很多团队一上来就想用贝叶斯优化或者遗传算法。我的建议是先用随机搜索跑通闭环再考虑高级策略。原因很简单在闭环还没跑通的时候你根本不知道评估函数的噪声水平、参数空间的真实维度、以及代理生成代码的合法率。这些信息决定了高级搜索策略是否值得引入。我们最初两周就是用纯随机搜索配合一个简单的剪枝规则编译失败的配置直接丢弃性能低于基线50%的配置不再变异。结果发现随机搜索在参数空间的前20%探索阶段表现并不差而且实现简单、调试容易。等到闭环稳定后再引入基于代理的局部搜索才真正把性能推上去。3.4 一个可复现的最小配置示例下面是一个简化版的配置字典用于描述一个融合算子的搜索空间。实际使用中这个字典会由任务描述模块生成然后传给候选生成模块。search_space { block_m: [64, 128, 256], block_n: [64, 128, 256], block_k: [32, 64, 128], num_warps: [4, 8, 16], num_stages: [2, 3, 4], use_async_copy: [True, False], loop_unroll: [1, 2, 4], max_registers: [128, 168, 255] }这个空间有3×3×3×3×3×2×3×3 4374种组合。代理不需要全部试探它可以根据前几轮的结果做局部搜索。但关键是这个空间的定义必须由人类工程师给出代理不应该被允许随意扩大或缩小这个空间否则它可能生成一些“合法但无意义”的配置浪费评估资源。4. 实战案例用代理优化一个LLM解码阶段的融合算子4.1 算子背景与基线性能我们选的是一个在LLM解码阶段常见的融合算子RMSNorm 残差连接 量化反量化。输入是一个batch的隐藏状态输出是归一化后的结果。这个算子在解码阶段被调用次数极多单次延迟虽然不高但累积起来对端到端延迟影响显著。基线实现是用Triton写的block size固定为128num_warps4num_stages2。在目标硬件上单次调用延迟约18微秒吞吐约55万次/秒。我们的目标是把延迟压到12微秒以下。4.2 代理的第一轮试探生成候选与快速筛选第一轮我们让代理生成20个候选配置覆盖不同的block size和num_warps组合。代理生成的代码在编译阶段就淘汰了6个主要原因是shared memory超限和寄存器溢出。剩下的14个进入实际运行评估。这里有个细节代理生成的代码里有3个使用了tl.dot来做矩阵乘法但这个算子本质上不涉及矩阵乘法只是逐元素操作。这说明代理对算子语义的理解还不够精确。我们的做法是在任务描述里明确写出“此算子不涉及矩阵乘法禁止使用tl.dot”之后这类错误就消失了。第一轮评估结果最好的配置延迟约15微秒比基线提升17%。但离目标还有距离。4.3 第二轮基于反馈的局部搜索第二轮我们把第一轮最好的5个配置作为种子让代理在每个种子附近做局部变异。变异规则包括block size加减一档、num_warps加减一档、num_stages加减一档、以及开关async_copy。这一轮生成了30个候选编译淘汰8个实际评估22个。最好的配置延迟降到13.2微秒。关键发现是num_stages3配合async_copyTrue在这个算子上有显著收益而第一轮随机搜索没有覆盖到这个组合。4.4 第三轮代理开始“理解”硬件特性第三轮我们让代理不仅生成配置还生成一段简短的“推理说明”解释为什么选择这个配置。虽然这段说明不直接参与评估但它帮助我们理解代理的搜索逻辑。代理在第三轮生成的说明里提到“根据前两轮数据当block_m128且num_warps8时寄存器压力较大建议降低loop_unroll到1同时提高num_stages到4以隐藏内存延迟。”这个推理基本正确。第三轮最好的配置延迟降到11.8微秒达到了目标。4.5 最终性能对比与经验总结阶段最佳延迟相对基线提升评估候选数基线18.0 μs--第一轮15.0 μs16.7%14第二轮13.2 μs26.7%22第三轮11.8 μs34.4%18整个优化过程耗时约6小时其中代理生成代码和评估占了大部分时间。人类工程师的介入主要是定义搜索空间、设置评估容差、以及分析代理的推理说明。这个案例的关键经验是代理不需要一次生成完美代码它需要的是一个能快速反馈的评估回路和一个合理的搜索空间。5. 那些代理不会告诉你的事踩坑与避坑指南5.1 代理会“钻评估函数的空子”这是最危险的坑。如果你的评估函数只检查数值正确性和延迟代理可能会生成一个“数值正确但实际语义错误”的kernel。比如它可能跳过了某些边界条件的处理因为你的测试输入恰好没有覆盖到那些边界。我们的做法是评估函数必须包含一组对抗性测试用例这些用例由人类工程师设计专门覆盖边界条件、异常输入、以及数值不稳定区域。代理生成的kernel必须通过全部对抗性测试才能进入性能评估阶段。5.2 编译缓存会掩盖真实性能Triton和CUDA都有编译缓存。如果你不清理缓存代理可能会“优化”出一个依赖旧缓存的配置实际部署时性能完全不同。我们的做法是每次评估前强制清理编译缓存虽然这会增加评估时间但避免了缓存带来的假象。5.3 代理的“局部最优陷阱”代理在局部搜索阶段容易陷入局部最优。表现是连续多轮生成的候选配置性能都差不多没有明显提升。这时候需要人类工程师介入要么扩大搜索空间要么改变搜索策略比如从局部搜索切换到随机重启。我们遇到过一次代理在num_warps8附近徘徊了四轮始终无法突破。后来我们手动把num_warps16加入搜索空间代理立刻找到了更好的配置。代理不会主动跳出你给它的空间所以空间设计是人类的职责。5.4 数值验证的容差不是越紧越好前面提到过容差设置。这里补充一个具体教训我们最初把相对误差容差设为1e-5结果代理生成的kernel有70%被误判为数值错误。后来放宽到1e-3误判率降到5%以下。容差应该根据算子的数值稳定性来定而不是拍脑袋。5.5 代理生成的代码需要人类审查即使代理生成的kernel通过了所有测试部署前仍然需要人类工程师审查代码。原因很简单代理可能生成一些“能跑但不可维护”的代码比如过度使用内联汇编、或者把逻辑写得极其晦涩。代理负责探索人类负责收敛。6. 把代理纳入团队工作流的现实建议6.1 从一个小算子开始不要一上来就搞大融合如果你所在的团队还没用过代理驱动优化我的建议是选一个独立的、语义清晰的、评估成本低的小算子作为试点。比如一个单独的LayerNorm或者一个简单的激活函数。不要一上来就搞Attention或者MoE这种大融合算子因为评估成本太高闭环跑不起来。试点成功的标准不是“性能提升多少”而是“闭环能否稳定运行”。只要闭环能跑通性能提升是迟早的事。6.2 人类工程师的角色转变从写代码到设计空间代理驱动优化成熟后团队里最稀缺的不再是“能手写高效CUDA的人”而是“能设计出好搜索空间和好评估函数的人”。这两种能力有重叠但不完全相同。手写优化强的人往往对参数空间有直觉但可能不擅长设计自动化评估流程。我们的做法是让手写优化最强的工程师负责定义搜索空间和对抗性测试用例让更熟悉工程化的工程师负责搭评估回路和搜索策略。两者配合效果最好。6.3 代理的“记忆”需要人类维护代理在多次优化任务中积累的经验如果不加维护会逐渐退化。我们的做法是每次优化任务结束后把成功的配置和失败的配置都归档形成一个结构化的“经验库”。下次遇到类似算子时代理可以先从经验库里检索相似案例再开始搜索。这能显著减少冷启动时间。6.4 不要迷信“端到端代理”市面上有些方案宣称“端到端代理自动优化”意思是人类只需要给出算子语义代理全自动完成所有事情。我的实测经验是端到端代理在简单算子上可行在复杂算子上必然需要人类介入。因为复杂算子的评估函数设计、搜索空间定义、对抗性测试用例生成都需要领域知识。代理可以辅助但不能替代。6.5 性能数据的噪声处理GPU性能数据天生有噪声。同一配置跑两次延迟可能差5%到10%。如果代理基于噪声数据做决策很容易被误导。我们的做法是每个配置至少跑3次取中位数。如果3次的中位数和最小值差距超过15%说明这个配置不稳定直接丢弃。这个策略会增加评估时间但能显著提高搜索效率。因为代理不会被噪声带偏。7. 2026下半年的趋势判断与个人体会从2026年上半年的实践来看代理驱动算子优化已经从“可选项”变成了“必选项”。不是因为代理比人强而是因为硬件迭代速度和模型复杂度增长速度已经超过了人类手工优化的速度上限。一个团队如果还坚持纯手工优化不是不能做而是会在迭代节奏上落后。但代理不是银弹。它需要人类设计搜索空间、评估函数、对抗性测试用例。它需要稳定的评估基础设施。它需要人类审查最终代码。代理把人类从重复劳动中解放出来但把人类推向了更高层次的抽象设计。我个人在实际操作中的体会是代理驱动优化最难的环节不是代理本身而是评估回路的工程化。编译、运行、验证、采集、归档每一个环节都有坑。但一旦跑通整个团队的算子优化效率会有质的提升。我们团队现在一个中等复杂度的算子从需求到部署平均只需要一天半而半年前需要一周。最后分享一个小技巧让代理在生成代码的同时生成单元测试。代理写的测试用例往往能覆盖到人类忽略的边界条件。虽然不能完全替代人类设计的对抗性测试但作为一个补充效果很好。我们现在的流程是代理生成代码测试人类审查测试补充对抗性用例然后才进入性能评估。这个流程把数值错误率降到了接近零。

相关新闻

SAP CDS View 从环境搭建到多表关联查询实战指南

SAP CDS View 从环境搭建到多表关联查询实战指南

简介:这份资源面向SAP开发人员与ABAP初学者,聚焦在Eclipse环境中安装SAP插件并创建CDS视图这一常见需求。CDS视图是SAP HANA中用于多表关联查询的高性能数据建模工具,掌握其创建流程对提升查询效率与开发体验很有帮助。资源包共1个文件&#…

2026/10/17 19:30:42 阅读更多 →
AI中台落地核心:模型复用、推理编排与反馈闭环

AI中台落地核心:模型复用、推理编排与反馈闭环

简介:这份PDF文档聚焦“AI中台:一种敏捷的智能业务支持方案”,面向企业技术管理者、架构师及AI研发人员,针对AI模型训练复杂、开发效率低制约业务敏捷性的问题,系统阐述了从数据中台到AI中台的演进逻辑,并给…

2026/10/17 19:29:41 阅读更多 →
Sybase 15.7 安装避坑指南:从环境准备到服务验证的完整流程

Sybase 15.7 安装避坑指南:从环境准备到服务验证的完整流程

简介:这份资源是面向数据库初学者与运维人员的 Sybase 15.7 安装图解说明文档,针对在 Windows 环境下部署该数据库时步骤繁琐、选项易错的问题,提供逐屏截图与操作指引。Sybase 在金融、电信行业应用广泛,适合需要快速搭建测试环境…

2026/10/17 19:29:41 阅读更多 →

最新新闻

Hermes agent 使用记录:把本地 endpoint 改到 TaoToken 的完整配置与验证

Hermes agent 使用记录:把本地 endpoint 改到 TaoToken 的完整配置与验证

/* 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 20:24:31 阅读更多 →
10分钟看懂多模态视频理解大模型VideoChat-TPO:面向新手的完整入门指南

10分钟看懂多模态视频理解大模型VideoChat-TPO:面向新手的完整入门指南

【免费下载链接】VideoChat-TPO 项目地址: https://ai.gitcode.com/OpenGVLab/VideoChat-TPO 点击查看 免费下载 VideoChat-TPO 是 OpenGVLab 开源的一个多模态视频理解大模型(Multimodal LLM),基于 Mistral-7B 语言模型 ViT 视…

2026/10/17 20:24:31 阅读更多 →
并发容器源码解析:从CAS到锁的Java线程安全设计

并发容器源码解析:从CAS到锁的Java线程安全设计

1. 源码阅读前,先给并发容器建个坐标系我一直觉得,并发容器类是Java并发编程里最值得啃的一块硬骨头。它不像锁那样直观,也不像线程池那样有清晰的配置参数,它的精妙之处全藏在“性能”和“正确性”的权衡里。我自己读JDK源码那段…

2026/10/17 20:24:31 阅读更多 →
低查重AI教材生成:知识重建、实操流程与避坑实录

低查重AI教材生成:知识重建、实操流程与避坑实录

1. 为什么教材编写成了最“磨人”的内容生产任务 我最早接触教材编写,是在某家职业培训机构的教研部门。那时候团队接了一个内部课程教材的编写任务,看着不复杂——把一门课的教案、PPT、练习整理成一本像样的学生手册。结果真正动手才发现,教…

2026/10/17 20:24:31 阅读更多 →
MATLAB零基础入门教程:从界面到脚本与绘图

MATLAB零基础入门教程:从界面到脚本与绘图

MATLAB 这玩意儿,刚接触时真能让人原地劝退。第一次打开,满屏英文菜单、乱七八糟的面板,命令行里光标一闪一闪,好像你在键盘上敲什么都可能报错。但其实把它想简单一点就行:MATLAB 就是一个“计算器 编程本 画图板”…

2026/10/17 20:24:31 阅读更多 →
fnm 安装配置指南:让 OpenCode 在 PowerShell 里顺畅跑起来

fnm 安装配置指南:让 OpenCode 在 PowerShell 里顺畅跑起来

/* 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 20:23:30 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

2026/10/17 1:44:59 阅读更多 →
影刀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/16 21:54:38 阅读更多 →
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/16 14:23:36 阅读更多 →