1. 面试官为什么总爱问 Embedding 和 LLM 输出的关系1.1 这道题到底在考什么面 Java AI 岗位的时候很多人一听到“Embedding 和 LLM 输出向量到底啥关系”就懵了。有人觉得这俩是一回事都是“把东西变成一串数字”有人觉得 Embedding 是 EmbeddingLLM 输出是 LLM 输出八竿子打不着。这两种理解都不对而且面试官恰恰就是冲着这个模糊地带来的。先把结论摆在前面Embedding 和 LLM 输出向量形式上都是浮点数数组但它们的语义来源、产生方式、用途完全不同。Embedding 是“把输入映射到语义空间”LLM 输出向量是“模型在生成过程中对下一个 token 的预测分布logits或者隐藏状态”。一个是编码器思维一个是解码器思维。面试官问这道题本质是在确认三件事你分不分得清“表示”和“生成”你知不知道向量在 RAG、语义检索、Agent 记忆里怎么用你能不能把原理落到 Spring AI 的代码上。我面过不少人能把float[]和ListDouble的区别讲清楚但一问“为什么余弦相似度能算语义相似”就开始背公式了。所以这篇我打算从原理讲到生产代码把这条链路彻底打通。1.2 一个生活化类比先建立直觉你可以把 Embedding 想成“图书馆的索引卡”。每本书一段文本、一张图、一段音频被压缩成一张卡片卡片上写着它在语义空间里的坐标。内容相近的书坐标就挨得近。这个坐标是静态的、可复用的你查一次存下来下次直接比对就行。LLM 输出向量则更像“一个人边说话边在大脑里形成的下一个词的概率草图”。它不是一个固定的坐标而是模型在当前上下文下对“接下来该说什么”的内部表示。你让它生成一句话它每一步都会产出一个高维向量logits这个向量经过 softmax 才变成词的概率。这个向量是动态的、上下文相关的、一次性的。所以关键差异在于Embedding 是“输入侧”的表示LLM 输出向量是“输出侧”的表示。前者用来检索、聚类、去重后者用来采样、生成、决策。混为一谈RAG 就会做歪。1.3 为什么 Java 开发者尤其容易踩坑Java 生态里做 AI主流路径是 Spring AI 或者 Spring AI Alibaba。这两套框架把 Embedding 和 Chat 抽象成了两个不同的接口EmbeddingModel和ChatModel。但很多同学写代码的时候看到EmbeddingModel.embed()返回float[]看到ChatModel.call()返回String就以为 LLM 不产向量。其实 LLM 内部每一步都在产向量只是框架帮你封装掉了。面试官问这道题往往是想引出下一个问题“那你怎么用 LLM 的输出向量做语义缓存”或者“RAG 里为什么用 Embedding 而不是直接用 LLM 输出”能答上来的人基本就过了 AI 应用开发这一关。2. 把 Embedding 和 LLM 输出向量的原理彻底拆开2.1 Embedding 的本质把离散符号压进连续空间Embedding 这个词最早来自词向量。Word2Vec 时代我们把每个词映射成一个 100 到 300 维的稠密向量让“国王 - 男人 女人 ≈ 女王”这种语义运算成立。到了 Transformer 时代Embedding 不再只是词级别而是句子、段落、图像、音频都能编码。它的核心机制是通过一个可学习的矩阵把离散的 token id 映射成连续向量再经过多层编码器比如 BERT、BGE、text-embedding-3聚合出整段文本的表示。这个表示通常取[CLS]token 的隐藏状态或者对所有 token 做 mean pooling。关键点在于Embedding 模型是专门为“表示”训练的。它的训练目标可能是对比学习让相似文本的向量靠近不相似的远离也可能是掩码语言建模。不管哪种最终产出的向量有一个重要性质语义相似度可以用余弦相似度或点积直接衡量。这就是为什么 RAG 里能用它做检索。你把知识库所有 chunk 提前 embed 好存进向量数据库用户提问时把问题 embed 成同维度向量做最近邻搜索就能召回语义相关的内容。整个过程不需要 LLM 参与快且便宜。2.2 LLM 输出向量的本质下一个 token 的概率分布LLM 是自回归模型。给定上下文[t1, t2, ..., tn]它预测下一个 tokentn1的概率分布。这个分布怎么来的最后一层 Transformer 输出一个隐藏状态h然后经过一个线性层LM Head映射到词表大小V的向量也就是 logits。logits 经过 softmax 就变成概率。所以严格来说LLM 每一步的输出向量是 logits维度等于词表大小比如 32000、128000。这个向量不是用来算语义相似度的它是用来采样下一个词的。你取 argmax 就是贪心解码按概率采样就是随机解码加温度就是调节分布平滑度。那 LLM 的隐藏状态能不能当 Embedding 用可以但效果通常不如专门的 Embedding 模型。因为 LLM 的训练目标是生成不是表示。它的隐藏状态里混杂了大量生成相关的信息语义纯度不够。学术界有人拿 LLM 最后一层隐藏状态做检索效果能打但工程上不划算——LLM 推理成本比 Embedding 模型高一个数量级。2.3 一张表看清两者的核心差异维度EmbeddingLLM 输出向量产生位置编码器输出 / 池化层解码器最后隐藏状态 / LM Head典型维度384、768、1024、1536、3072等于词表大小32000 到 128000语义性质静态、可复用、语义空间坐标动态、上下文相关、生成分布主要用途检索、聚类、去重、推荐采样、生成、决策、打分相似度计算余弦相似度、点积、欧氏距离一般不直接算相似度成本低可批量离线高逐 token 自回归框架接口EmbeddingModel.embed()ChatModel.call()内部这张表建议背下来。面试时能脱口而出面试官基本就知道你是真做过项目的。2.4 为什么 RAG 必须用 Embedding 而不是 LLM 输出有人会问既然 LLM 也能产向量为什么 RAG 检索不用 LLM 输出原因有三。第一成本。Embedding 模型参数量通常在 1 亿到 10 亿之间LLM 动辄几百亿。把 10 万条知识 chunk 全部过一遍 LLM账单能让你怀疑人生。第二语义纯度。Embedding 模型是对比学习训出来的天生适合算相似度。LLM 的隐藏状态是为生成服务的里面混了位置、语法、生成策略等信息直接拿来检索召回率会掉。第三维度与存储。Embedding 通常 768 到 1536 维向量数据库存起来很轻松。LLM 隐藏状态动辄 4096 到 8192 维存储和检索成本都高。所以工程上的标准做法是检索用 Embedding生成用 LLM两者通过向量数据库解耦。这也是 Spring AI 里VectorStore和ChatClient分开设计的原因。3. Spring AI 生产级代码实战从 Embedding 到 RAG 全链路3.1 环境准备与依赖选型先明确技术栈。我这次用的是 Spring Boot 3.2 Spring AI 1.0.0-M6 Spring AI AlibabaEmbedding 模型选的是通义千问的text-embedding-v3LLM 选的是qwen-plus。向量数据库用 Redis Stack因为大部分 Java 团队已经有 Redis加个 RediSearch 模块就能当向量库用运维成本最低。Maven 依赖如下dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-core/artifactId version1.0.0-M6/version /dependency dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter/artifactId version1.0.0-M6.1/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-redis-store/artifactId version1.0.0-M6/version /dependency配置文件里把 API Key 和模型名配上spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} embedding: options: model: text-embedding-v3 chat: options: model: qwen-plus temperature: 0.7 data: redis: host: localhost port: 6379注意API Key 千万别硬编码进代码用环境变量或者配置中心。我见过有人把 Key 提交到 Git第二天就被刷爆了。3.2 用 EmbeddingModel 把文本变成向量Spring AI 的EmbeddingModel接口非常干净核心就两个方法embed(String)和embed(ListString)。批量接口很重要因为生产环境你不可能一条一条 embed那样网络往返开销太大。Service public class EmbeddingService { private final EmbeddingModel embeddingModel; public EmbeddingService(EmbeddingModel embeddingModel) { this.embeddingModel embeddingModel; } public float[] embedSingle(String text) { return embeddingModel.embed(text); } public Listfloat[] embedBatch(ListString texts) { return embeddingModel.embed(texts); } }这里有个细节text-embedding-v3默认输出 1024 维但支持通过dimensions参数降到 512 或 256。降维能省存储和检索时间但会损失一点精度。我的经验是知识库小于 10 万条时用 1024 维超过 100 万条再考虑降维。批量 embed 的时候要注意单次请求的 token 上限。通义千问的 Embedding 接口单次最多 25 条文本每条不超过 2048 token。所以生产代码里要自己做分批public Listfloat[] embedInBatches(ListString texts, int batchSize) { Listfloat[] result new ArrayList(); for (int i 0; i texts.size(); i batchSize) { int end Math.min(i batchSize, texts.size()); ListString batch texts.subList(i, end); result.addAll(embeddingModel.embed(batch)); } return result; }3.3 向量入库与相似度检索拿到float[]之后下一步是存进向量数据库。Spring AI 的VectorStore抽象帮你屏蔽了底层差异Redis、Milvus、PgVector 都能用同一套 API。Service public class KnowledgeBaseService { private final VectorStore vectorStore; public KnowledgeBaseService(VectorStore vectorStore) { this.vectorStore vectorStore; } public void addDocuments(ListString chunks) { ListDocument documents chunks.stream() .map(chunk - new Document(chunk, Map.of(source, manual))) .toList(); vectorStore.add(documents); } public ListDocument search(String query, int topK) { SearchRequest request SearchRequest.query(query) .withTopK(topK) .withSimilarityThreshold(0.7); return vectorStore.similaritySearch(request); } }similarityThreshold这个参数很关键。设太低会召回一堆不相关的内容污染 LLM 的上下文设太高又可能漏掉关键信息。我的经验值是 0.65 到 0.75 之间具体要看 Embedding 模型的分布。你可以先用一批标注数据跑一下召回率和准确率再定阈值。3.4 把检索结果喂给 LLM 生成答案RAG 的最后一步是把召回的 chunk 拼进 prompt让 LLM 基于这些上下文回答。Spring AI 的ChatClient提供了 fluent API写起来很顺。Service public class RagService { private final ChatClient chatClient; private final KnowledgeBaseService knowledgeBaseService; public RagService(ChatClient.Builder builder, KnowledgeBaseService knowledgeBaseService) { this.chatClient builder.build(); this.knowledgeBaseService knowledgeBaseService; } public String answer(String question) { ListDocument docs knowledgeBaseService.search(question, 5); String context docs.stream() .map(Document::getContent) .collect(Collectors.joining(\n\n)); String prompt 你是一个严谨的技术助手。请仅基于以下上下文回答问题。 如果上下文没有相关信息直接说我不知道不要编造。 上下文 %s 问题%s .formatted(context, question); return chatClient.prompt() .user(prompt) .call() .content(); } }这段代码里有几个生产级的考量。第一prompt 里明确要求“仅基于上下文”减少幻觉。第二要求“不知道就说不知道”避免模型硬编。第三topK 设 5 是经验值太多会超上下文窗口太少召回不足。3.5 用 LLM 输出向量做语义缓存前面说了 LLM 输出向量一般不直接用于检索但有一个场景例外语义缓存。传统缓存用 key 精确匹配用户问“Java 怎么创建线程”和“Java 线程创建方式”会命中两次 LLM浪费钱。语义缓存的做法是把用户问题 embed 成向量先在缓存里找相似问题如果相似度超过阈值直接返回缓存答案。Service public class SemanticCacheService { private final EmbeddingModel embeddingModel; private final MapString, CachedAnswer cache new ConcurrentHashMap(); public OptionalString get(String question) { float[] queryVec embeddingModel.embed(question); return cache.values().stream() .filter(entry - cosineSimilarity(queryVec, entry.vector()) 0.92) .map(CachedAnswer::answer) .findFirst(); } public void put(String question, String answer) { float[] vec embeddingModel.embed(question); cache.put(question, new CachedAnswer(vec, answer)); } private double cosineSimilarity(float[] a, float[] b) { double dot 0, normA 0, normB 0; for (int i 0; i a.length; i) { dot a[i] * b[i]; normA a[i] * a[i]; normB b[i] * b[i]; } return dot / (Math.sqrt(normA) * Math.sqrt(normB)); } }阈值 0.92 是我实测下来的经验值。低于这个值容易把不同问题误判为相同高于 0.95又太严格缓存命中率低。生产环境建议用 Redis 存向量别用ConcurrentHashMap否则重启就丢。4. 常见问题与排查技巧实录4.1 Embedding 维度对不上怎么办这是新手最容易遇到的问题。你用一个模型 embed 了知识库换模型之后查询报维度不匹配。原因是不同 Embedding 模型输出维度不同text-embedding-v3是 1024 维text-embedding-v2是 1536 维BGE 是 768 维。解决办法只有一个换模型必须重新 embed 整个知识库。没有捷径。所以选模型的时候要慎重尽量选稳定、长期维护的。我一般会在项目初期就把 Embedding 模型定死写进技术文档避免后期换模型带来的全量重建。4.2 相似度很高但答案不对这种情况通常是 chunk 切分有问题。你把一整篇文档 embed 成一个向量语义被平均掉了检索时召回的是“整体相关”但“细节不对”的内容。正确做法是按语义边界切分比如按段落、按标题层级切每个 chunk 控制在 200 到 500 token。Spring AI 提供了TokenTextSplitterTokenTextSplitter splitter new TokenTextSplitter(500, 100, 5, 10000, true); ListDocument chunks splitter.apply(documents);参数含义分别是目标 chunk 大小、重叠 token 数、最小 chunk 数、最大 chunk 数、是否保留分隔符。重叠 100 token 是为了避免句子被切断导致语义丢失。4.3 LLM 不按上下文回答模型幻觉是 RAG 的老大难。除了 prompt 里明确约束还有几个技巧。第一把上下文放在问题前面模型对前面的内容注意力更强。第二给每个 chunk 加编号让模型引用来源。第三用 few-shot 示例告诉模型“不知道就说不知道”。如果还是不行考虑换更强的 LLM或者降低 temperature。temperature0时模型最保守适合事实性问答temperature0.7适合创意生成。RAG 场景我一般设 0.1 到 0.3。4.4 常见问题速查表问题现象可能原因排查方向解决方案维度不匹配换过 Embedding 模型检查模型名和维度配置全量重新 embed召回不准chunk 切分过大看 chunk 平均 token 数按语义边界切分加重叠答案幻觉prompt 约束不足检查 prompt 模板加“仅基于上下文”约束响应慢向量库没建索引看 Redis 慢查询日志建 HNSW 或 IVF 索引缓存命中低阈值设太高统计相似度分布调到 0.90 到 0.93成本高每次全量 embed看 API 调用量加语义缓存批量 embed4.5 几个我踩过的坑第一个坑批量 embed 时没控制并发。我一开始用parallelStream并发调 Embedding 接口结果触发限流一半请求失败。后来改成固定线程池并发度控制在 5 以内稳定了。第二个坑向量没归一化。余弦相似度和点积在向量归一化后等价但如果你用点积检索又没归一化结果会偏。Spring AI 的VectorStore内部会处理但自己写相似度计算时要注意。第三个坑Redis 向量索引没预热。Redis Stack 建 HNSW 索引后第一次查询会慢因为要构建图。生产环境建议启动后跑一批预热查询。第四个坑prompt 太长超上下文。topK 设 10每个 chunk 500 token加上问题本身轻松超过 8000 token。后来我把 topK 降到 5chunk 控制在 300 token问题解决。5. 面试怎么答这道题才能加分5.1 标准回答框架如果面试官问“Embedding 和 LLM 输出向量啥关系”你可以这样答“两者形式上都是浮点数组但语义来源不同。Embedding 是编码器对输入的语义压缩静态可复用专门为相似度计算训练维度通常 768 到 1536。LLM 输出向量是解码器对下一个 token 的预测分布动态上下文相关维度等于词表大小用于采样生成。工程上RAG 检索用 Embedding生成用 LLM两者通过向量数据库解耦。Spring AI 里分别对应EmbeddingModel和ChatModel。”这段话覆盖了定义、差异、用途、框架落地面试官基本满意。5.2 加分项主动聊语义缓存和 Agent 记忆如果面试官继续追问你可以主动引出语义缓存“LLM 输出向量虽然不直接用于检索但可以用于语义缓存。把用户问题 embed 后比对历史问题相似度超过 0.92 直接返回缓存答案能省不少 token 成本。”再进一步可以聊 Agent 记忆“Agent 的长期记忆也可以用 Embedding 实现。把历史对话 embed 存起来每轮对话前检索相关记忆注入上下文让 Agent 记住用户偏好。”这两个点一抛出来面试官就知道你不只是背八股是真做过项目。5.3 反问环节可以问什么面试快结束时你可以反问“咱们团队的 RAG 系统用的是什么 Embedding 模型有没有做语义缓存”这个问题既显示你的专业度又能帮你判断团队的技术水平。如果对方答不上来说明 AI 应用还在早期你进去有机会主导如果对方讲得头头是道说明团队有积累你能学到东西。6. 写在最后的一些个人体会我做 Java AI 应用这两年最大的感受是原理不清代码白写。很多人上来就抄 RAG demo跑通了就以为会了。结果一上生产召回不准、幻觉严重、成本爆炸全是坑。根子就在于没搞懂 Embedding 和 LLM 输出向量的本质区别。Spring AI 把复杂度封装得很好但封装不等于你可以不懂。面试官问这道题不是要你背概念是要确认你有没有能力在出问题时定位到根因。维度不对、召回不准、缓存失效这些问题的答案都藏在对向量的理解里。最后分享一个小技巧如果你要深入学这块别只看文档自己动手 embed 一批文本算一下相似度矩阵看看哪些该近的近了、哪些该远的没远。跑一遍比看十篇文章都管用。向量这东西直觉是跑出来的不是看出来的。
企业数字化 ERP 产品动态
相关推荐
UE5 JSON解析与序列化:FJsonObject与FJsonValue详解 1. UE5 JSON解析核心类解析在Unreal Engine 5的C开发中,处理JSON数据是常见的需求。UE5提供了一套完整的JSON处理类库,其中最核心的就是FJsonObject和FJsonValue这两个类。理解它们的结构和关系,对于高效处理JSON数据至关重要。1.1 FJsonObje… · 2026/11/3 8:43:30
Word文档跨平台排版差异解析与解决方案 1. 为什么同一个Word文件在不同编辑器里排版会不一样? 这个问题困扰着无数办公人士,尤其是那些需要跨平台协作的团队。作为一名经历过无数次文档格式灾难的老编辑,我清楚地记得上周五下午5点半,当我把精心排版的报告发给客户后&am… · 2026/11/3 8:43:30
S7-200 SMART状态图表调试实战:监视策略、强制边界与排故链路 1. 状态图表到底在调试环节扮演什么角色搞过PLC现场调试的人都清楚一个现实:程序写完了、下载进去了、设备也动起来了,但"动得对不对"完全是另一回事。尤其是用西门子S7-200 SMART这类小型控制器做产线改造或者单机设备控制的时候,… · 2026/11/3 8:43:30
Swisslog百年转型:从电气小厂到物流自动化巨头的历程 1. Swisslog百年转型史:从配电小厂到物流自动化巨头的蜕变1900年,瑞士阿尔劳小镇上,两位电气工程师创立了一家名为SprecherSchuh的小型配电设备厂。谁曾想到,这家最初生产电气控制柜的小作坊,会在125年后发展成为全球物… · 2026/11/3 16:35:57
VMware搭建Linux环境全攻略与性能优化 1. 为什么选择VMware搭建Linux环境 在本地计算机上运行Linux系统,虚拟机方案始终是开发者和运维人员的首选。相比双系统安装,VMware Workstation这类虚拟化软件提供了更灵活的解决方案——既能保留原有操作系统,又能获得完整的Linux体验。 我… · 2026/11/3 16:35:57
Prometheus告警规则模板:从资源到应用,告别误报与噪音 告警规则这件事,我见过太多团队栽在同一个坑里:Prometheus 部署得漂漂亮亮,Grafana 大盘也画得有模有样,结果真出故障的时候,要么一条告警都没响,要么半夜被三百条重复通知炸醒,爬起来一看全是同… · 2026/11/3 16:35:57
CAN物理层7项硬核测试实操指南 简介:本资源是一份面向高校电子/车辆工程专业学生及嵌入式开发初学者的CAN总线物理层测试实践文档,聚焦车载通信硬件可靠性验证核心能力培养。文档系统覆盖终端电阻、高低压通信范围、显性/隐性输出电压、上升沿与下降沿时间等7项关键物理层测试… · 2026/11/3 16:35:57
Verilog状态机设计:从题目抽象到可综合RTL的实战指南 1. 从一道“其他状态机题目”说起:为什么这类题总让人卡壳做Verilog笔试题或者课程设计的时候,很多人都有过这种体验:一段式、两段式、三段式状态机写得滚瓜烂熟,交通灯、序列检测、自动售货机这些经典题目也能默写出来࿰… · 2026/11/3 16:35:57
用C++从零手写CNN恶意软件检测:完整实现与踩坑记录 作为一个平时主要用 Python 写深度学习、但偏偏要在某些场景下用 C 把模型落地的人,我对“C 深度学习”这几个字真是又爱又恨。这个系列前面聊了环境搭建和基础张量操作,这篇“第三弹”我想直接来点硬核的:不聊理论推导,而是逐步用… · 2026/11/3 16:34:55
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46