Transformer编码解码与token化:语义向量提取与相似度计算实操
我印象里最直观的一次是某次给一个跨语言检索项目做语义召回优化。我用“某预训练模型”跑完一个句子的编码把它的CLS向量取出来做相似度计算结果发现它跟一个看起来完全不相关的中文句子得分极高排在前面的结果也莫名相似。后来把中间层的token向量全部拉出来做了个可视化和方差分析才发现问题出在模型顶层输出分布过于各向异性很多维度其实都是死维度根本不参与语义区分。那次之后我对“编码解码、token化、语义向量”这三个词背后的机制就格外敏感——它们不是三个独立概念而是同一条流水线上依次衔接的三道工序。这篇文章我想站在实操角度把transformer里编码和解码两侧到底各自在做什么、token化流程怎么影响最终的语义向量质量、向量又是如何一步步被“榨”出来的掰开来写清楚。适合刚接触NLP的初学者用来建立整体认知也适合已经上手但对内部细节还没完全吃透的人快速补齐盲区。1. 编码解码架构不是所有模型都长一个样在真正上手抓向量之前最好先对transformer的架构分岔有个清醒认识。因为并不是所有transformer模型都同时拥有编码器和解码器它们的分工和产出物差异极大直接决定了你取到的“语义向量”到底代表什么。1.1 三种变体与各自的产出物第一类是编码器专属架构典型代表是某基础预训练模型家族。它的职责是把输入token序列映射成一串上下文相关的向量序列输出层每个token对应一个向量。这类模型适合做语义理解、文本分类、检索召回等任务因为它的双向注意力机制让每个token都能同时看到左右两侧的上下文编码出的向量携带的信息密度较高。第二类是解码器专属架构典型代表就是GPT这类模型。它使用因果注意力训练目标是从左到右预测下一个token。解码器同样会产出每个token的向量但每个位置只能看到它之前的内容所以向量携带的是一种“基于前文条件”的语义。这类模型适合做生成任务但如果直接用它的隐状态做语义相似度效果可能不如编码器因为它的建模目标是预测下一个词而非完整理解全句语义。第三类是完整的编码器-解码器架构像原始的sequence-to-sequence模型系列。编码器负责把源序列压缩成上下文表示解码器每个位置通过交叉注意力从编码器侧拉取信息逐步生成目标序列。这类架构在翻译、摘要任务上仍然有优势。从实操角度看抓语义向量首选编码器架构如果非要拿解码器模型做嵌入至少要做到两点一是把提示词和待编码文本当作一个整体丢进去二是取最后一个token的向量。因为只有最后一个token能看到全部前文信息其他位置的向量都“近视”。1.2 掩码机制编码与解码最大的行为分水岭双向注意力和因果注意力在实现上的区别说白了就是一个注意力掩码矩阵。编码器在标准自注意力下每个token对序列中所有其他token的注意力权重都允许非零解码器则强加一个上三角掩码把未来位置全部置为负无穷经过Softmax归一化后注意力权重趋近于0。这个机制对语义向量的影响是深层次的。我在做某些长文本分类任务时做过一组对照实验同一个句子分别用编码器模型和因果解码器模型取CLS或最后一个token向量做分类前者在样本量不大时效果明显更稳。原因在于编码器在计算第3个token的表示时已经提前“偷看”了第10个token的信息使得这个位置的向量在一开始就融入了全局语境而解码器第3个token的向量则完全不知道第10个token会说什么。差异还体现在序列级聚合策略上。编码器模型里[CLS]位或均值池化都能用解码器模型里最好使用最后一个token或者对位置靠后的token加权平均因为靠前的token向量严重“目盲”拉进均值里反而稀释语义。2. token语义向量从离散符号到稠密空间讲清楚了架构差异接下来要把token本身的语义向量是怎么形成的拆明白。一个token从一个离散的ID变成一个能参与相似度计算的稠密浮点数向量中间其实经过了嵌入层、位置编码注入、多层注意力加工三个主要阶段。2.1 token化粒度选择如何影响向量质量token化是整条流水线的入口。它的本质是把文本切成模型词汇表里能对应的最小单位。中文场景下最容易踩的坑就是“按字切”还是“按词切”的纠结。现在主流模型普遍采用子词分词算法比如BPE或WordPiece它会根据语料统计频率自动找出一批最常见的子词单元。举个例子“嵌入表示”在某种分词器下可能被切成“嵌”、“入”、“表示”三个token另一种词表则可能把它切成“嵌入”、“表示”。这两种切法的差异会直接影响语义向量的质量切得越碎每个token的单独语义越模糊模型必须靠上下文慢慢“修补”切得越整token的语义本体越强但遇到生僻复合词时词汇表会急剧膨胀。从实操角度讲在做中文任务时我会先跑一遍tokenizer看看句子被切成什么样再决定后续特征怎么处理。比如对“自然语言处理”这个短语如果tokenizer把它切成了“自然”、“语言”、“处理”那么取第2个token的向量做短语级表示通常比取第1个强因为“语言”在中间位置能同时吸收左右语境。这种细节只有实际打开tokenizer看输出才知道。2.2 嵌入层到上下文向量每个step在干什么token进入模型后先通过嵌入矩阵查表得到一个初始向量这个向量只包含该token本身的语义完全没有上下文信息。然后会加上一个位置编码向量因为注意力机制本身是不关心顺序的如果不加位置信息“我爱你”和“你爱我”在初始输入阶段会得到完全相同的向量序列。接下来进入多层transformer模块。每一层内部大致做四件事自注意力或掩码注意力、残差连接、层归一化、前馈网络。自注意力负责让每个token去“询问”其他token并按照相关性权重聚合信息残差连接保证梯度可以顺畅回传层归一化稳定数值分布前馈网络则对每个位置独立做非线性变换相当于在注意力汇总结果之上再做一层特征精炼。经过多层这样迭代加工每个token的向量逐渐从“孤立的词表向量”变成“融合了全文语境的上下文向量”。这也是为什么说transformer堆叠层数越多向量语义越细腻——但也不是越多越好考虑到算力与过拟合一般实践里取中间层或倒数第二层的小节位置往往反而比最后一层好用。3. 如何提取和检验语义向量的质量3.1 提取位置的选择逻辑实际操作中最常见的需求是把一整句话变成一个向量再利用向量做相似度检索或分类。提取方式通常有三种每种背后的逻辑完全不同得分效果差异也大。第一种是CLS池化。某些大型预训练模型在序列开头放置一个特殊的“分类用”token它本身不承载具体词义经过多层编码后变成整个序列的“摘要向量”。CLS向量优势是省事劣势是当文本过长时容易丢失细节信息。第二种是均值池化。把所有token的隐状态在序列维度上取平均得到一个固定维度向量。这种策略在短文本上表现不错因为它把每个位置的语义都纳入了考量缺点是无法自动区分哪些token重要。第三种是加权池化。根据每个token与某个目标概念的相关性给它分配权重再加权求和。在检索场景里我常用注意力权重或TF-IDF来加权效果比普通均值更可控。比如做某个关键词的相似搜索时先把关键词对应的token权重拉高再做池化得到的向量会明显朝该方向偏移。经验上短文本几十个token以内用均值池化或CLS都行中等长度文本建议取CLS加均值拼接长文本则要考虑分段编码再加总。我踩过最典型的坑一段含300个token的法律条款文本直接取CLS向量做检索结果召回的是“法条标题”相似但“具体罚则”无关的条目换成分段均值池化后才恢复正常。原因就是CLS向量被标题与开头部分的强语义带跑了尾部细节信息被稀释殆尽。3.2 标准化、维度与相似度计算细节拿到向量后第一步习惯性要做L2标准化。原因有两个一是很多相似度计算如余弦相似度本身对模长敏感度不一标准化后统一在单位球面上比较方向物理含义更干净二是很多向量空间里存在“高频方向”干扰标准化能从一定程度上压低模长异常向量带来的干扰。维度选择方面常规预训练模型输出的隐状态维度一般在768或1024左右。工程上我做过一个对比把768维向量用PCA压到128维再做检索速度提升了近3倍但召回率几乎没掉因为原始维度里存在大量冗余相关维度。如果你做的是超大规模向量检索强烈建议先降维再做索引不必执着于原始维度。相似度计算时还有一个细节常被忽略某些模型输出的向量空间呈现“各向异性”表现为几乎所有向量都聚集在少数几个方向上此时直接用余弦相似度会让不相关的句子得分虚高。解决办法可以是对向量做均值中心化减去全体语料的平均向量后再做标准化或者直接改用负欧氏距离即向量越近得分越高来替代余弦。我实测下来在检索类任务中中心化后的余弦相似度比原始余弦稳定不少。4. 实操环节用现成工具跑通一条完整编码链路理论说再多不如实际跑一遍。下面我用PyTorch生态里最常用的库来演示如何快速加载一个预训练模型、两行代码完成token化、拿到token级隐状态并组装成可用的句子级语义向量。4.1 环境准备与基础调用首先需要安装必要的库。如果你在本地跑建议用虚拟环境隔离依赖避免不同项目包版本互相冲突。pip install transformers torch这里推荐用某预训练模型底座因为它的权重相对稳定、文档齐全、中文生态也友好。代码姿态上第一件事是加载分词器和模型from transformers import AutoTokenizer, AutoModel tokenizer AutoTokenizer.from_pretrained(某预训练模型底座) model AutoModel.from_pretrained(某预训练模型底座) model.eval()加载后先别急着喂数据我一般会先检查一下tokenizer的vocab大小和模型配置的hidden_size确保两个组件的版本匹配。曾经遇到过本地缓存的tokenizer版本与模型权重版本不一致导致token id错位生成的向量全部无效。这种问题最大特点是代码不报错但结果诡异——排查起来极耗时间检查版本一致性永远是第一步。4.2 完整编码流程与中间量提取下面这段代码演示了从原始文本到token级向量、再到句子级向量的完整过程import torch # 1. 文本结构化 text transformer的编码器与解码器各自承担不同职责 # 2. 分词 拼接特殊标记 encoded tokenizer( text, max_length128, truncationTrue, paddingmax_length, return_tensorspt ) # 3. 关闭梯度节省显存 with torch.no_grad(): outputs model(**encoded, output_hidden_statesTrue) # 4. 提取最后一层隐状态形状: (batch, seq_len, hidden_dim) last_hidden outputs.last_hidden_state # 5. 提取CLS向量开头位置 cls_vec last_hidden[:, 0, :] # 6. 提取均值池化向量建议忽略padding部分 attention_mask encoded[attention_mask] expanded_mask attention_mask.unsqueeze(-1).float() sum_vec (last_hidden * expanded_mask).sum(dim1) token_counts expanded_mask.sum(dim1) mean_vec sum_vec / token_counts # 7. L2标准化 cls_vec torch.nn.functional.normalize(cls_vec, p2, dim-1) mean_vec torch.nn.functional.normalize(mean_vec, p2, dim-1)这段代码里值得展开讲的有三个细节。第一max_length和truncation的选择。如果文本实际长度远超128截断会让尾部语义丢失如果远小于128padding后会浪费大量计算在无效位置上。实操上我会先统计语料长度分布取85%分位作为max_length既减少截断损失又不至于过分拉长训练时间。第二均值池化时必须用attention_mask配合。直接用last_hidden.mean(dim1)会把padding位置内容全是0或特殊填充向量也平均进去严重拉低向量质量。我见过不止一个人在这个地方踩坑最后相似度分数一片飘低排查半天才发现是padding未过滤。第三output_hidden_statesTrue是必须的开关。如果不打开这个参数outputs里只有last_hidden_state拿不到每个transformer层的隐状态也就无法做中层向量提取或层间对比实验。很多进阶玩法如层间加权、早停解码策略都依赖这个参数。4.3 层间加权与语义向量的进阶玩法既然能拿到所有层的隐状态就可以做一些更精细的加工。比如在文本相似度任务上不同层捕获的信息粒度不同——浅层更关注词法与局部搭配中层更关注句法结构与短语语义深层更关注全局语义与逻辑关系。一种常用的做法是层间加权求和而不是只用最后一层。权重可以来自经验值也可以通过学习得到。经验值上我试过给倒数第2层、倒数第3层各分配0.3的权重中间层分配0.2其余层均分剩余0.2效果比只用最后一层稳定不少。原因在于最后一层隐状态在某些情况下过度适配预训练任务的损失函数作为通用特征反而有点“偏科”。代码上只需多一个循环hidden_states outputs.hidden_states # 包含embedding层输出共13个元素12层embedding weighted_vec torch.zeros(hidden_states[0].shape[-1]) for i, h in enumerate(hidden_states[1:], start1): layer_weight 0.2 if i in [len(hidden_states)-2, len(hidden_states)-3] else 0.1 weighted_vec layer_weight * h[:, 0, :].squeeze()需要提醒的是层间加权并非对所有任务都是“越复杂越好”。在新闻分类这种任务上最后一层表现往往最直接在语义相似度、文本聚类这类任务上层间加权才明显占优。所以我习惯的做法是先跑一个快速基线用最后一层再对比层间加权的提升幅度最后再决定上线方案。5. 常见问题与排查技巧实录章节 | 问题现象 | 排查思路与方法 | 避坑心得第一维度对不上或形状报错。常见于多卡推理或批量处理时没有保持tensor形状一致。解决方法是打印模型的config.hidden_size再结合batch中的seq_len、mask形状逐一核对。遇到维度报错不要盲目调代码先打印所有相关tensor的shape做一次全面体检。第二相似度结果虚高。所有文本得分都在0.95以上完全区分不开。这种情况通常意味着向量没有经过中心化或标准化且模型输出层分布高度各向异性。解决方法是先对所有向量做batch内中心化再L2标准化最后用余弦相似度虚高问题会缓解很多。第三序列长度超过模型上限。比如某模型的最大位置编码只有512但文本有800个token直接输入会报错或截断。方案有三种一是用滑动窗口分段编码每段分别出向量再加权累加二是使用支持长文本的模型或其他结构三是先用句子分割把长文本切块再对块级向量做平均。实际生产环境中第三种方案最常用且稳定。第四tokenize后token数与模型期望不一致。典型表现是中文标点、全角空格处理方式不同导致序列长度或ID错位。排查时先print出tokenizer输出的ids和对应文本逐条对照。尽量避免手动改tokenizer配置用官方分词器内置参数达到需求是最省事的做法。第五CPU推理慢到怀疑人生。如果你只是少量文本需要编码或只是做实验验证直接在CPU上跑也可以但务必设置torch.no_grad()并对模型调用eval()。这两个操作能省下大量内存与时间。若要处理大规模语料建议用GPU并开启批量推理配合半精度加载模型可显著提速。6. 实操路线建议与后续扩展思路6.1 如果向量质量不够好先自己怀疑三步如果相似度结果总是不尽如人意先别急着换模型按顺序排查三个环节分词粒度是否适合语料、提取层是否有偏差、池化策略是否掩盖了关键token语义。这三步走完绝大多数向量质量问题都能定位到具体环节。就分词粒度而言也不一定“切得越细越好”。某些医疗领域的术语必须整词切分才能保留意义但默认词表未必覆盖。如果目标语料高度垂直可以额外做一次自定义词典增强让分词器优先把术语切成整词后者编码得到的向量语义显著更准。池化策略则是很多初学者的盲区。CLS、均值、加权三种策略在不同任务上差距可以很大。我建议准备一套小型的验证集每个策略都跑一遍用任务指标选优不要凭感觉猜测。6.2 语义向量的后续工程化扩展向量拿到手紧接着就是存储和检索。工程上百万级向量用向量数据库或普通文件配FAISS都能应对。低于十万级用NumPy文件加暴力检索即可十万到千万级建议上FAISS或专用向量数据库更大规模再考虑分布式方案。另一个值得尝试的方向是融合多向量表达。短文本用句子级向量长文本用分段向量求平均再拼上一个加权向量多路召回后做分数融合。这种方式在检索精度上通常会比单路强一大截。最后关于模型选择和个人经验如果任务强调语义相似度优先选编码器架构且训练目标含对比学习的模型或相关底座如果任务偏生成或对话那么解码器或完整编解码结构才是你的选择若条件允许针对垂直语料做一次小规模微调能让token向量的分布贴近你的数据领域相似度质量的提升往往超出预期。

相关新闻

Python接口自动化测试实战:pytest+requests+yaml数据驱动框架

Python接口自动化测试实战:pytest+requests+yaml数据驱动框架

做接口自动化的同学,绕不开这一套组合:Python 写脚本、requests 发请求、pytest 跑用例、yaml 存数据。前面两篇我们搭好了环境,也把 pytest 的常用姿势过了一遍,这一篇作为系列第三篇,我不打算再讲基础语法&#xff0…

2026/10/18 12:22:22 阅读更多 →
Vibe Coding实战:用AI编程助手打造极简日常记录小程序

Vibe Coding实战:用AI编程助手打造极简日常记录小程序

“vibe coding”这个词,从去年开始频繁出现在各种技术帖里。前半段热度很高,后半段褒贬不一,多数人觉得这是“糊弄式编程”的遮羞布。但我不这么看——它更像一种新的交互方式:你把脑子里模糊的画面,原原本本描述给AI&…

2026/10/18 12:22:22 阅读更多 →
Vue3+TS构建企业级AI对话引擎:流式输出、图表渲染与Agent编排

Vue3+TS构建企业级AI对话引擎:流式输出、图表渲染与Agent编排

1. 项目概述:这不是一个“聊天框”,而是一套可嵌入业务流程的智能对话引擎Vue3 TS 构建企业级 AI 智能对话系统——光看标题,很多人第一反应是“又一个带 loading 动画的 ChatUI”。但实际落地时你会发现,真正卡住团队进度的&…

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

最新新闻

【OpenClaw】创建一个每日热点新闻 Skill:从 SKILL.md 到定时任务与网关配置

【OpenClaw】创建一个每日热点新闻 Skill:从 SKILL.md 到定时任务与网关配置

/* 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 13:09:48 阅读更多 →
C++内存管理:智能指针、定制Allocator与生产级观测实践

C++内存管理:智能指针、定制Allocator与生产级观测实践

简介:本资源是Roy Patrice撰写的《C Memory Management》(2025年出版)英文原版PDF电子书,面向中高级C开发者、系统程序员及参与ISO C标准实践的技术人员,聚焦内存管理这一影响程序安全性、性能与可维护性的核心能力。全…

2026/10/18 13:09:48 阅读更多 →
baltika源码拆解:从DispatcherServlet到JSP的Spring MVC完整调用链

baltika源码拆解:从DispatcherServlet到JSP的Spring MVC完整调用链

简介:javaweb框架源码-baltika是一份基于Spring Framework编写的Java Web应用开源源码项目,面向想通过读源码吃透Spring MVC请求处理流程、依赖注入、AOP与事务管理等核心机制的初中级Java开发人员,也适合准备进行框架级源码分享的进阶学习者…

2026/10/18 13:09:48 阅读更多 →
STM32温湿度采集与无线传输实战:从DHT11驱动到低功耗状态机

STM32温湿度采集与无线传输实战:从DHT11驱动到低功耗状态机

简介:这份资源是一份基于STM32的温湿度检测与无线传输毕业设计文档,面向自动化、电子信息及嵌入式方向的在校学生与初学者,帮助其理解从数据采集到无线通信的完整设计思路。文档围绕STM32单片机展开,涵盖芯片选型、CC1020无线模块…

2026/10/18 13:09:48 阅读更多 →
C# WinForms 摄像头采集:AForge.NET 拍照与避坑指南

C# WinForms 摄像头采集:AForge.NET 拍照与避坑指南

简介:本资源面向具备一定C#基础的Winform开发者与计算机视觉入门者,聚焦使用Aforge框架调用摄像头并完成拍照这一常见需求,帮助读者快速搭建可运行的摄像头采集与拍照Demo。包内为1个PDF文档,压缩包约177KB,内容以图文…

2026/10/18 13:09:48 阅读更多 →
单片机教学闭环试卷:从寄存器操作到硬件时序验证

单片机教学闭环试卷:从寄存器操作到硬件时序验证

简介:本资源是一份面向高校计算机类专业学生的《单片机原理及应用》与《微机原理及应用》课程配套考试试卷(A卷)及完整参考答案,适用于期末复习、考前自测与知识点查漏补缺。试卷涵盖MCS-51单片机结构、寻址方式、指令系统、堆栈操…

2026/10/18 13:08: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/18 0:00:55 阅读更多 →

周新闻

基于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/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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 阅读更多 →