规格驱动开发这个词最近在团队里被反复提起起因很简单我们用 AI 编码助手写代码写得越快返工越多。需求在脑子里提示词随手一写AI 生成一大坨代码跑起来看着像那么回事一对接真实业务就崩。问题不在于模型不够强而在于我们从来没把规格当成一等公民。OpenSpec 就是冲着这个痛点来的——它把规格文档变成开发流程的中枢让 AI 助手围绕一份可执行、可校验的规格去写代码而不是围绕一段模糊的自然语言提示。这篇内容适合已经在用 Claude Code、Codex CLI 这类命令行 AI 编码工具但被生成快、返工也快折磨过的开发者也适合想给团队建立一套可复用 AI 开发规范的技术负责人。我会从它解决的问题讲起一路拆到目录结构、工作流、和 CLI 工具的配合方式以及我自己踩过的那些坑。1. 为什么先写规格这件事在 AI 编码时代突然变得关键1.1 提示词工程的瓶颈到底卡在哪过去两年大家谈 AI 编码焦点几乎都在提示词上怎么写得更清楚、怎么加 few-shot 示例、怎么让模型理解上下文。但真正上手做项目的人会发现提示词工程有个绕不过去的天花板——它是一次性的、易失的、不可累积的。你今天写了一段精妙的提示词让 AI 生成了一个用户认证模块明天换个会话、换个人这段提示词就消失了没人知道当时约束了什么、边界在哪、为什么这么设计。更麻烦的是提示词天然是对话式的。你跟 AI 说帮我写个登录功能它会自己脑补一堆假设用 JWT 还是 Session密码怎么存错误码怎么定义这些假设它不会告诉你直接写进代码里。等你发现不对改起来就是连锁反应。我见过最夸张的一次一个团队用 AI 生成了整套订单系统结果发现 AI 默认用了软删除而他们的数据库规范要求物理删除加归档表最后重构花了三天。规格驱动开发的核心洞察是与其反复优化提示词不如把约束沉淀成一份结构化的规格文档。这份文档既是给人看的也是给 AI 看的。它不随会话消失可以进版本控制可以 review可以增量修改。OpenSpec 做的就是把这件事工程化。1.2 OpenSpec 把规格放在了什么位置理解 OpenSpec 的关键是搞清楚它在整个开发链路里的位置。传统的 AI 编码流程是人 → 提示词 → AI → 代码规格是隐式的、散落在提示词里的。OpenSpec 把它改成人 → 规格文档 → AI → 代码 → 校验 → 回写规格。这里有个很重要的设计取向OpenSpec 不是又一个代码生成器它更像是一个规格的容器和调度层。它定义了一套目录约定和文件格式让规格、变更提案、任务清单、实现代码之间有明确的对应关系。你可以把它理解成给 AI 编码加了一层项目管理只不过管理对象不是人而是 AI 的工作上下文。它和 Claude Code、Codex CLI 这类工具的关系是互补的。CLI 工具负责执行——读文件、改代码、跑命令OpenSpec 负责定义做什么、为什么做、做到什么程度算完成。没有 OpenSpecCLI 工具就是个执行力很强但没方向的实习生有了 OpenSpec它才像个知道验收标准的工程师。1.3 规格驱动和测试驱动、文档驱动的区别很多人第一反应是这不就是文档驱动开发吗或者测试驱动开发的变体其实差别挺大。测试驱动开发TDD的核心是先写测试测试即规格。但测试有个问题——它描述的是结果对不对不描述为什么这么做。AI 拿到一堆测试能写出通过的代码但可能写出一个完全违背架构意图的实现。而且测试对还没实现的功能表达能力很弱你很难用测试描述一个交互流程的设计意图。文档驱动开发的问题是文档和代码容易脱节写完就没人看了变成摆设。OpenSpec 的规格不是纯文档它是带结构的、可被工具解析的、和代码目录有映射关系的。规格里的每个需求、每个场景都能对应到具体的实现文件和验证方式。这就避免了文档写完就腐烂的老问题。规格驱动开发更像是把需求分析和验收标准这两件事从人的脑子里、从会议记录里搬到一个 AI 和人都能读的中间层。它不替代测试也不替代文档而是给这两者提供一个共同的源头。2. OpenSpec 的目录结构与文件约定拆解2.1 核心目录布局长什么样OpenSpec 的目录设计是它最值得学的地方因为它直接决定了 AI 能不能找得到上下文。一个典型的 OpenSpec 项目结构大致是这样project-root/ ├── openspec/ │ ├── specs/ # 已确认的规格按能力域拆分 │ │ ├── auth/ │ │ │ └── spec.md │ │ └── order/ │ │ └── spec.md │ ├── changes/ # 进行中的变更提案 │ │ └── add-password-reset/ │ │ ├── proposal.md │ │ ├── tasks.md │ │ └── design.md │ └── project.md # 项目级约定与背景 ├── src/ └── ...这个结构里specs/存的是当前系统是什么样changes/存的是我们打算改成什么样。这个区分非常关键它对应了软件工程里现状和变更的分离。AI 在处理一个任务时先读specs/了解现状再读changes/里对应的提案了解目标最后才动手写代码。project.md是全局约定比如技术栈、代码风格、命名规范、禁止使用的库等等。我建议把团队所有口头约定都塞进这里因为 AI 不会读你的群聊记录但它会读这个文件。2.2 规格文件里到底该写什么规格文件spec.md不是随便写写需求就完事。它有一套推荐的结构核心是**需求Requirement和场景Scenario**两层。一个需求描述系统应该具备什么能力一个场景描述在什么条件下系统应该有什么行为。举个认证模块的例子## Requirement: 用户密码登录 系统应支持用户通过邮箱和密码登录。 ### Scenario: 正确的凭据 - Given 一个已注册且状态正常的用户 - When 用户提交正确的邮箱和密码 - Then 系统返回访问令牌 - And 令牌有效期不超过 24 小时 ### Scenario: 密码错误 - Given 一个已注册用户 - When 用户提交错误的密码 - Then 系统返回 401 错误 - And 不泄露用户是否存在的信息这种 Given-When-Then 的写法不是 OpenSpec 独创的但它在这里的作用变了——它不只是给人读的验收标准更是给 AI 的行为约束。AI 读到不泄露用户是否存在的信息就知道错误信息不能区分用户不存在和密码错误。这种细节如果你只在提示词里说写个登录AI 百分之百会漏掉。2.3 变更提案为什么单独放一个目录changes/目录是 OpenSpec 里我觉得最聪明的设计。每个变更是一个独立文件夹里面有提案、任务清单、设计说明。这样做的好处是变更是有生命周期的。一个变更从提出到完成会经历提案 → 实现 → 归档三个阶段。完成后这个变更文件夹会被合并进specs/然后从changes/里移除。这意味着changes/目录永远只包含正在进行的工作AI 读它的时候不会被历史包袱干扰。tasks.md是任务清单把一个大变更拆成可执行的小步骤。这个文件对 AI 特别有用因为它给了 AI 一个明确的进度条。你可以让 AI 读tasks.md完成一项就勾掉一项这样即使中途换会话AI 也能从上次停下的地方继续。design.md是可选的用来记录技术决策和权衡。比如为什么选 Redis 而不是数据库存 session这种决策写下来AI 在后续实现里就不会自作主张换方案。3. 把 OpenSpec 接进 Claude Code 和 Codex CLI 的实际做法3.1 为什么不能只靠 CLI 工具的默认行为Claude Code 和 Codex CLI 这类工具默认的工作方式是你给指令它读代码它改代码。它们很聪明但有个共同问题它们倾向于在局部做最优解而不是在全局做一致解。举个例子你让 Codex CLI 给订单模块加个折扣功能它会去看订单相关的代码然后加一个折扣字段、一段计算逻辑。但它不会主动去检查这个折扣规则和现有的促销模块冲突吗数据库迁移脚本写了吗API 文档更新了吗因为这些信息不在它读到的文件里。OpenSpec 的作用就是把这些它读不到的信息变成它能读到的文件。你不需要改变 CLI 工具的行为只需要在项目里放好 OpenSpec 目录然后在提示里告诉 AI先读 openspec/specs/order/spec.md 和 openspec/changes/add-discount/proposal.md再动手。3.2 给 AI 的提示词该怎么组织接入 OpenSpec 之后提示词的组织方式会发生根本变化。以前你写的是帮我实现 X 功能现在你写的是按照 openspec/changes/xxx 的提案实现遵守 openspec/project.md 的约定。我常用的提示词模板大概是这样请先阅读以下文件 1. openspec/project.md项目约定 2. openspec/specs/相关域/spec.md现状 3. openspec/changes/当前变更/proposal.md 和 tasks.md目标 然后 - 按 tasks.md 的顺序逐项实现 - 每完成一项在 tasks.md 里标记完成 - 实现过程中如果发现规格有歧义先停下来问我不要自行假设 - 实现完成后对照 spec.md 里的 Scenario 逐条自检这个模板的关键在于最后两条。发现歧义先问能避免 AI 自作主张对照 Scenario 自检能让它自己发现遗漏。实测下来加了这两条之后AI 一次生成就能跑通的比例明显上升。3.3 不同 CLI 工具的适配差异Claude Code 和 Codex CLI 在配合 OpenSpec 时行为上有一些差异值得注意。Claude Code 对长上下文和文件结构的理解更好它能一次性读多个文件并建立关联。所以你可以让它一次读整个changes/目录。但它有时候会过度解读把规格里没写的东西也脑补进去所以约束要写死。Codex CLI 更偏向精确执行你让它改哪个文件它就改哪个不太会主动扩展。这在配合 OpenSpec 时反而是优点——规格说什么它做什么。但缺点是它不太会主动去读你没明确提到的文件所以提示里必须把要读的文件列全。我的做法是用 Claude Code 做规格梳理和方案设计用 Codex CLI 做按规格实现。前者负责把模糊需求变成清晰规格后者负责把清晰规格变成代码。分工明确之后两边都不容易跑偏。4. 规格驱动开发里最容易踩的几个坑4.1 规格写太细反而把 AI 框死刚开始用 OpenSpec 的时候我犯过一个典型错误把规格写得像伪代码。每个函数签名、每个字段类型都写进去结果 AI 变成了翻译器完全没有发挥空间生成的代码死板且冗余。后来我调整了粒度规格写行为和约束不写实现细节。比如密码必须用 bcrypt 加盐哈希存储这是约束要写密码哈希函数叫 hashPassword接收 string 返回 Promise这是实现细节不写。前者是 AI 必须遵守的后者是 AI 可以自己决定的。判断标准很简单如果这个细节变了会不会影响其他模块或违反业务规则会就写进规格不会就留给实现。4.2 变更和规格不同步导致的规格腐烂这是最隐蔽的坑。团队用着用着有人直接改代码不改规格几次之后specs/里的内容和实际代码就对不上了。AI 读到过时的规格生成的代码自然也是错的。解决办法有两个层面。流程上把更新规格作为代码合并的必选项就像写测试一样。工具上可以在 CI 里加一个检查对比规格里声明的接口和实际代码的接口是否一致。OpenSpec 本身不强制这个但它的结构让这种校验变得可行。我自己的习惯是每次变更归档时强制自己花五分钟过一遍specs/确认没有遗漏。这五分钟能省掉后面几小时的排查。4.3 把 OpenSpec 当成必须完整的负担还有一种坑是心态上的。有人觉得既然用了规格驱动那每个功能都得写完整规格结果小改动也要走一遍完整流程效率反而低了。实际上 OpenSpec 是分层的。大变更走完整流程提案、设计、任务、规格。小改动比如改个文案、调个样式完全可以跳过changes/直接改代码。规格的价值在于约束那些改错了代价很大的部分不是约束所有部分。我一般这么判断这个改动如果做错了会不会影响数据、安全、或者多个模块的交互会就写规格不会就直接干。5. 从零搭一套 OpenSpec 工作流的完整步骤5.1 初始化项目结构第一步是把目录建起来。不需要什么脚手架手动建就行mkdir -p openspec/specs openspec/changes touch openspec/project.md然后往project.md里写项目级约定。这个文件我建议包含这几块技术栈和版本、代码风格缩进、命名、注释语言、目录约定、禁止事项比如不要引入新的第三方库除非明确要求、以及 AI 协作时的通用指令。project.md不用一次写全边用边补。每次发现 AI 犯了同类错误就把对应的约束加进去。用久了它会变成团队的AI 协作手册。5.2 写第一份规格选一个你熟悉的、边界清晰的模块开始比如用户资料管理。在specs/下建对应目录写spec.md。写的时候记住几个原则用 Given-When-Then 描述场景每个需求独立成节约束要具体可验证响应时间小于 200ms比响应要快好不要写实现细节。第一份规格不用追求完美它的作用是让你熟悉这套表达方式。写完可以拿给 AI 读一遍问它这份规格有没有歧义或遗漏AI 往往能指出一些你自己没注意到的模糊点。5.3 走一遍完整的变更流程建一个changes/下的变更文件夹写proposal.md为什么改、改成什么样、tasks.md拆成可执行步骤、可选的design.md技术决策。然后让 AI 按tasks.md实现。实现过程中你会遇到两种情况一种是规格清晰AI 顺利做完另一种是规格有歧义AI 停下来问。后者其实是好事说明规格在起作用——它把隐式假设变成了显式问题。变更完成后把proposal.md里的内容合并进对应的spec.md然后删掉changes/下的这个文件夹。这一步别偷懒它是保持规格新鲜的关键。5.4 和版本控制、CI 的配合OpenSpec 目录应该进 Git和代码一起管理。这样每次变更都有迹可循review 的时候也能看到规格改了什么、代码改了什么。CI 层面可以加一些轻量检查规格文件是否符合格式约定、changes/里有没有长期未归档的变更、规格里提到的接口是否在代码里存在。这些检查不用很复杂一个脚本就能搞定但能有效防止规格腐烂。6. 规格驱动开发在团队协作中的真实收益与边界6.1 收益从人盯人到规格盯人用了一段时间之后最大的感受是协作方式变了。以前 AI 生成的代码要 reviewreview 的人得先理解需求再判断代码对不对很累。现在 review 的对象变成了规格——规格对不对、全不全这个判断比看代码快得多。规格确认了代码只要符合规格基本就没大问题。另一个收益是新人上手快了。新成员进来不用先读一堆代码先读specs/就能理解系统在做什么。规格是比代码更高层的抽象理解成本低很多。6.2 边界什么场景不适合规格驱动规格驱动不是万能的。探索性开发、原型验证、一次性脚本这些场景写规格就是浪费时间。规格的价值在于约束而探索阶段恰恰需要的是自由。还有一种情况是需求极度不稳定今天定了明天就变。这种时候写规格也是白写不如先让 AI 快速出个能跑的东西等需求稳定了再补规格。我的判断标准是这个功能会不会长期存在、会不会被多人维护、改错了代价大不大。三个都是是就值得写规格有一个是否就可以考虑跳过。6.3 和现有工程实践的融合OpenSpec 不需要推翻你现有的流程它可以叠加在 Scrum、看板、或者任何你已经在用的方法上。它管的是AI 协作这一层不替代项目管理工具。实际落地时我建议先在一个小项目或一个模块上试点跑通完整流程积累几份规格样本再推广到团队。直接全团队铺开很容易因为不熟悉而流于形式。7. 一些实操中总结的细节技巧7.1 规格里的反例比正例更有价值写场景的时候大家习惯写正常情况应该怎样。但 AI 最容易出错的地方恰恰是异常路径。所以每个需求我至少写一个异常场景参数缺失、权限不足、并发冲突、外部依赖失败。这些场景写进去AI 生成的代码健壮性会明显提升。7.2 用规格文件当AI 的记忆锚点长会话里 AI 会忘事这是通病。解决办法是定期让它重读规格。我一般在每个大任务开始前都让 AI 重新读一遍project.md和相关的spec.md相当于给它刷新记忆。这个动作花不了几秒但能显著减少跑偏。7.3 规格的版本要和代码版本对齐如果项目有多个长期维护的版本分支规格也要跟着分支走。别让所有分支共用一份规格那样迟早对不上。简单做法是规格文件跟着代码一起 cherry-pick虽然麻烦点但比事后排查强。7.4 定期做规格体检每隔一段时间比如每个迭代结束花半小时过一遍specs/删掉过时的、合并重复的、补充遗漏的。规格和代码一样不维护就会腐烂。这个体检不用很正式但要坚持。7.5 别让规格变成写给领导看的文档这是心态问题。规格是给 AI 和开发者用的工具不是汇报材料。所以它应该务实、具体、可执行而不是漂亮、全面、政治正确。如果一份规格写出来主要是为了展示那它大概率对实际开发没帮助。8. 关于工具选型和落地节奏的一点个人看法工具层面OpenSpec 本身很轻没有强依赖你甚至可以用纯文本加约定来实现类似效果。它的价值不在工具本身而在那套规格先行、变更分离、场景驱动的方法论。所以别纠结于是不是要用 OpenSpec 这个工具而要关注有没有把规格当成一等公民。落地节奏上我的建议是小步快跑。先在一个模块上试跑通写规格 → AI 实现 → 归档这个闭环感受一下它带来的变化。如果确实减少了返工、提升了代码一致性再逐步扩大范围。如果发现某个场景下它反而拖慢效率就果断跳过别为了流程而流程。最后说个我自己的体会规格驱动开发真正改变的不是 AI 的能力而是我们对需求这件事的严肃程度。以前写代码前脑子里过一遍就动手现在被迫把想法写清楚。这个过程本身就会暴露很多原本被忽略的问题。AI 只是个放大器它把想清楚和没想清楚的差距放大了。规格驱动本质上是逼着我们先想清楚。
企业数字化 ERP 产品动态
相关推荐
变容二极管调频电路课程设计:从指标拆解到电路交付的完整方案 简介:这份课程设计文档面向电子信息、通信工程等专业学生及电子电路初学者,围绕变容二极管调频电路设计展开,帮助读者理解直接调频方案的原理与实现路径。内容涵盖方案选择、FM调制原理、变容二极管直接调频原理分析,以及LC电容三… · 2026/11/3 16:39:01
美业小店数字化转型:轻量化解决方案与应用实践 1. 美业小店数字化转型的必然趋势 2026年的美业市场已经发生了翻天覆地的变化。作为一名在美发行业摸爬滚打8年的从业者,我亲眼见证了从"流量为王"到"效率制胜"的转变。现在的消费者不仅对服务质量要求更高,对消费体验的便捷性也愈发… · 2026/11/3 16:37:59
自动化工程师职业突破与质量保障体系构建 1. 自动化工程师的职业困境与价值重估在软件测试领域,我见过太多优秀的自动化工程师陷入职业发展瓶颈。他们日复一日地编写着重复的测试用例,像流水线上的工人一样机械地完成着测试任务。这种工作状态不仅消磨了他们的技术热情,更严重限制了职… · 2026/11/3 16:37:59
Whisper语音转写实战:从环境搭建到批量出稿的完整指南 1. 手动整理一小时的痛,和 Whisper 给我的转机先说说我为什么会专门写这篇 Whisper 语音转写实战。我手里头有大量音频要处理:每周至少两场客户访谈、一期播客、还有零零散散的会议录音。以前靠的是最原始的办法——听一句暂停一下,把重点敲进… · 2026/11/3 17:23:32
基于同步FIFO的位宽转换器设计:从8位到32位的Verilog实现与避坑指南 1. 从一个真实需求说起:为什么位宽转换绕不开FIFO做FPGA或者ASIC前端的朋友,几乎都遇到过一个场景:上游模块以8位宽度往外吐数据,下游模块却要求32位对齐输入,中间还夹着时钟域或者速率不匹配的问题。这时候最直接的想… · 2026/11/3 17:23:32
数据驱动配置化大屏开发:把大屏变成一份JSON配置 做前端时间长了,尤其是接触可视化大屏项目之后,我最大的感受就是:大屏这玩意,看着炫酷,做着想吐。倒不是说图表本身多难写,而是大量时间都耗在了“适配各种分辨率”、“反复调样式”、“后端数据一变就要跟… · 2026/11/3 17:23:32
TSM-Hub:面向LLM与Tools的统一能力调度网关 1. TSM-Hub 不是又一个“LLM 路由器”,而是技能执行层的基础设施重构你有没有试过这样一种状态:手头同时开着七八个 LLM 接口(OpenAI、Claude、Qwen、DeepSeek),调用着三四个本地 Tools(比如 Python 执行器… · 2026/11/3 17:23:32
合渲染收银台技术解析:高并发零售系统的性能优化 1. 项目背景与核心价值 "合渲染收银台"这个名词乍听有些专业,但简单来说就是通过整合多路渲染资源,实现高并发、高性能的收银系统界面呈现。我在零售行业数字化改造项目中摸爬滚打多年,见证过太多收银系统因为渲染性能不足导致卡顿… · 2026/11/3 17:23:32
企业安全框架OpenClaw:动态智能防护体系解析 1. 项目概述"龙虾安全指南"是奇安信面向2026年企业安全需求推出的全新安全框架体系。这个代号"OpenClaw"的项目本质上是一套融合了新一代安全技术的企业级解决方案,其核心目标是通过"看得清、管得住、用得好"三大维度,帮助… · 2026/11/3 17:22:30
双极步进电机控制实战: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