news 2026/9/7 5:37:55

AI编程代理安全落地:约束、审查与反馈闭环实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI编程代理安全落地:约束、审查与反馈闭环实战

最近团队里讨论 AI 编程代理(AI Coding Agent)的频率明显变高了。不少同学开始在真实项目里尝试让 AI 直接改代码、提 PR,但问题也随之出现:功能看着能跑,代码却经不起 review;局部需求实现了,整体架构却被带偏;提交记录里全是动辄几百行的大块改动,出了问题根本不知道从哪查起。

这段时间我结合 Figma 工程师在 AI Engineer 方向上的分享思路,加上自己在业务项目里的落地经验,整理了一套相对完整的落地方法。核心观点不复杂:AI 编程代理能不能安全落地,不取决于模型多强,而取决于你有没有在它前面立好“约束”,在它后面设好“审查”,在它周围搭好“反馈闭环”。这篇文章会把这套方法拆开讲,包含项目规则文件怎么写、提示词怎么约束、代码审查和 CI 怎么配合,以及最常见的几类翻车场景和排查思路。不管你是刚接触 AI 编程代理的开发者,还是准备在团队里推动落地,都可以直接参考。

1. 背景:AI 编程代理为什么会写出“垃圾代码”

1.1 先理解 AI 编程代理是什么

AI 编程代理和我们平时用的代码补全工具不太一样。代码补全工具(比如常见的 Copilot 补全模式)是在你写代码的过程中给出下一段建议,主动权在你手里;而 AI 编程代理是接收一个相对完整的任务描述后,自己去读代码、改文件、跑命令、甚至提交代码的“半个工程师”。

它通常具备几个能力:读取项目文件、理解仓库结构、跨文件修改、执行测试命令、根据报错自我修正、最后生成 diff。也正因为这些能力,它能完成的不再是“几行代码”级别的辅助,而是“一个功能点”甚至“一个模块”级别的任务。

听起来很美好。但问题恰恰出在这里:代理的能力边界越大,它闯祸的空间也越大。它可以在你不在场的情况下连续修改十几个文件,而你对它的改动意图、取舍逻辑、潜在影响,可能一无所知。

1.2 垃圾代码产生的三个根源

结合我自己项目的踩坑经验,AI 编程代理产出低质量代码,基本逃不出下面三个原因。

第一个是任务边界模糊。很多开发者给代理下达的指令是“帮我优化一下登录逻辑”或者“把这个页面改成响应式布局”。这种描述在人类同事听起来可能还能猜个大概,但代理会根据自己的理解“自由发挥”。它可能在你没想到的地方改了接口签名,可能把原本统一封装的请求函数替换成新的实现,也可能把一整套样式方案推翻重写。当你拿到 diff 时,发现改动范围远超预期,这就是典型的边界失控。

第二个是缺少领域的隐性知识。代码质量不只是“语法正确”和“功能跑通”。一个资深的团队工程师知道哪些模块是历史包袱不能乱动,知道某个工具函数在高并发下不能直接用,知道某个字段在数据链路里还有三个下游依赖。这些知识往往不在代码注释里,而在团队成员的脑海里。AI 代理没有这些背景,它只会按“最常规”的方式去写代码,于是经常出现“技术正确但业务错误”的改动。

第三个是反馈机制太慢。正常情况下,代码质量靠 code review 和测试兜底。但 AI 代理生成代码的速度太快了,如果团队没有在流程上强制加入审查节点,很容易出现“AI 写代码,人类只负责点合并”的情况。等问题暴露到测试环境甚至生产环境时,修 bug 的成本已经成倍上升。

1.3 不是所有代码都适合交给代理

这不是说 AI 编程代理不能用,而是要区分场景。从实践来看,下面几类任务适合交给代理:

  • 机械重复的改动,比如批量替换废弃 API、统一日志格式、给一组接口补充参数校验。
  • 单点明确的 bug 修复,比如某个函数在特定输入下抛异常,定位范围很小。
  • 测试代码补全,比如根据已有函数签名生成单元测试骨架。
  • 新项目脚手架搭建,比如创建规范的项目目录、初始化配置、编写基础 CRUD 代码。

这几类任务的共同点是边界清晰、影响面可控、有明确的验证标准。反过来,涉及核心架构调整、跨模块链路改造、历史兼容性判断的任务,现阶段还是应该由人来主导,代理最多负责辅助执行。

2. 安全落地的核心思路:先立约束,再谈效率

2.1 三条核心原则

我在反复踩坑之后,把 AI 编程代理的安全落地总结成三个原则:缩小授权范围、强制人工审查、建立反馈闭环

缩小授权范围,是指每次只给代理一个足够小的任务,而不是把一个需求完整丢给它。比如“给订单查询接口增加分页参数”就比“优化订单模块”安全得多。任务越小,代理的自由度越小,diff 越容易被人类看懂,出问题的概率天然就低。

强制人工审查,是指在代理生成代码之后、合并代码之前,必须有一个真正理解这段代码的人对改动负责。审查不是敷衍地看一眼,而是要回答三个问题:这个改动解决的是不是任务描述的问题?有没有引入不必要的连带修改?有没有违反项目里既有的设计约束?

建立反馈闭环,是指代理产生的每一次失败都应该沉淀为规则。比如它经常忘记处理空指针,那就在项目规则文件里明确写“所有可能为 null 的外部返回值必须判空”;它经常把工具函数复制到业务代码里,那就明确写“公共逻辑必须放到 shared 目录,不允许复制粘贴”。代理本身不会从错误中学习,但你的约束库会。

2.2 人类工程师的位置没有变

有些团队引入 AI 编程代理之后,容易产生一种错觉:以后写代码可以交给 AI 了,工程师只要描述需求就行。这种想法很危险。

在 Figma 工程师的分享里,有一个观点我印象很深:AI 编程代理改变的不是“要不要写代码”,而是“工程师把精力花在哪里”。以前你花大量时间在“敲代码”本身,现在这部分被压缩了,但“理解需求、拆解任务、定义约束、审查质量、解决边界问题”这些工作反而变得更重了。

换句话说,AI 编程代理是把工程师的位置从“生产者”推向了“审查者和架构师”。你不再对每一行代码负责,但你必须对每一个决策负责。如果你不理解它为什么这么改,不知道为什么这么改是对的还是错的,那你就没有资格让这段代码进入主干。

2.3 从“写代码”到“定义约束”

这套思路落到日常工作中,核心变化是:你的主要产出不再是一行行代码,而是一套让代码质量可控的约束规则。

约束可以分为两个层面。项目层面,通过AGENTS.md这类文件告诉代理“这个项目有哪些约定、哪些地方不能碰、完成任务需要走什么流程”;任务层面,通过提示词告诉代理“这次任务的目标是什么、边界在哪里、验收标准是什么”。

这两个层面的约束配合起来,才能把代理从“一个很会写代码但不懂事的实习生”变成一个“知道边界、按规范办事的执行者”。下面两章我会分别展开讲。

3. 团队级防护机制:规矩先写在前面

3.1 用 AGENTS.md 给代理立规矩

现在主流的 AI 编程代理都支持在项目根目录放一个规则文件,比如AGENTS.mdCLAUDE.md或者.cursor/rules/下的规则文件。代理在开始任务之前会先读取这些文件,把它当作“项目说明书”。这是目前成本最低、效果最明显的防护手段。

一个比较实用的AGENTS.md至少应该包含四块内容:

  • 项目结构和模块职责:让代理知道哪里有什么,避免在错误的位置修改。
  • 编码规范:命名、错误处理、日志、测试要求,越具体越好。
  • 禁止事项:哪些目录不能动,哪些模式不允许出现,哪些历史代码不要重构。
  • 工作流要求:先读哪些文件、改动上限、提交前必须做什么验证。

下面是一个参考示例:

# AGENTS.md ## 项目概述 这是一个订单管理后端服务,使用 TypeScript + Node.js 编写。 核心模块包括:订单模块、支付模块、用户模块、消息队列消费端。 ## 目录结构 - src/modules/orders/ 订单模块,包含 controller/service/repo - src/modules/payments/ 支付模块,禁止直接修改,改动需联系支付组 - src/shared/ 公共工具函数与公共类型 - src/config/ 环境配置与配置校验 - tests/ 单元测试与集成测试 ## 编码规范 - 所有文件使用 TypeScript 严格模式。 - 业务错误必须抛出 BizError,禁止抛出裸 Error。 - 所有外部输入必须经过 zod 校验。 - 日志使用 logger.info/logger.error,禁止使用 console.log。 - 公共逻辑统一放在 src/shared 下,禁止在业务代码中复制粘贴。 ## 禁止事项 - 禁止修改 src/modules/payments 下的任何文件。 - 禁止在仓库中新增全局可变状态。 - 禁止将多个业务逻辑写进一个函数,一个函数只做一件事。 - 禁止对历史模块做大规模重构,除非任务中明确要求。 ## 工作流要求 1. 开始任务前先阅读相关模块的 README 和现有测试文件。 2. 单次改动尽量控制在一个模块内,不要跨多个模块一起改。 3. 一次任务提交的改动量不超过 200 行,超出需要分多次完成。 4. 每次改动必须补充或更新对应测试,测试必须能通过。 5. 提交前运行 npm run lint 和 npm test,保证没有新增报错。

这个文件写清楚之后,代理在大部分情况下都会“照着规矩办事”。当然,它不是万能的,代理偶尔还是会忽略某些规则,但整体上能把那些最伤害项目的低级错误挡住一大半。

3.2 代码审查不许走过场

代码审查是安全落地的第二道防线,也是最关键的一道。代理生成代码的速度再快,只要审查这一关是硬的,垃圾代码就很难进入主干。

针对 AI 代理生成的代码,审查时要额外关注几个点。第一个是改动范围:这个 PR 里有没有跟任务无关的改动?比如任务只是加一个接口字段,结果它还顺手改掉了另一个文件的缩进风格,这种必须打回。第二个是重复代码:代理很倾向于“在当前位置写一份能用的代码”,而不是去复用已有的工具函数。审查时如果发现类似逻辑已经存在于 shared 目录,应该要求代理改写为复用。第三个是过度设计:有些代理为了“优雅”,会引入一层额外抽象,比如为一个简单的参数校验封装一个工厂函数。这种代码虽然能跑,但增加了维护成本,不符合项目现状时需要指出。

为了不让人工审查变成形式主义,我建议团队在 PR 模板里增加 AI 生成代码的专项检查项,强制审查者确认。示例:

### 审查清单 - [ ] 改动是否与任务描述一致,没有无关修改 - [ ] 是否复用了现有工具函数,而不是复制相似逻辑 - [ ] 是否引入了不必要的新依赖或抽象 - [ ] 错误处理是否符合项目规范(BizError / 日志 / 上下文) - [ ] 测试是否覆盖了正常路径和关键异常路径 - [ ] 是否存在安全风险(SQL 注入、越权、敏感信息泄露) - [ ] 是否使用最小权限原则访问外部资源

3.3 CI 自动化校验不能少

人工审查有遗漏的时候,CI 是最后的兜底。AI 代理生成的代码,在 CI 阶段需要至少跑三类检查:静态检查、测试覆盖、构建验证。

静态检查是成本最低的一关,比如 ESLint、TypeScript 类型检查、格式检查。代理经常会在缩进、命名、类型标注上出现小问题,这些交给工具自动拦截,不要浪费人工审查的时间。

测试覆盖这一关要特别强调。代理生成逻辑时,经常只保证“主流程能跑通”,对边界条件考虑不足。如果团队有覆盖率门槛,把它们加进 CI,低于阈值的 PR 不允许合并。

构建验证同样重要。很多代理只关注“我改的这个文件”,忽略整体构建,容易出现“单文件没毛病,整个项目编不过”的情况。CI 里加上构建步骤,可以第一时间暴露这类问题。

一个简单的 CI 配置参考如下:

name: ai-change-quality on: pull_request: types: [opened, synchronize] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm run lint - run: npx tsc --noEmit test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm test -- --coverage --coverage.threshold=80

4. 实战:一个可复制的 AI 编码工作流

4.1 把任务拆到“可审查”的粒度

前面的约束讲得再多,最后都要落到任务执行上。这里最值得花时间的环节,是任务拆分

我在实践中常用的拆分标准是:一个任务生成的 diff 应该能在 15 分钟内审查完,少于 200 行,并且只涉及一个模块。如果任务超出这个范围,就继续往下拆。

举个例子。需求是“给订单列表增加筛选状态和分页”。我不会直接把这个需求扔给代理,而是拆成三个连续任务:

  1. 在订单查询接口中新增 status 筛选参数,补充单元测试。
  2. 在订单查询接口中新增分页参数,返回分页结构,补充单元测试。
  3. 更新前端调用逻辑,展示筛选状态和分页控件。

每一步跑完,先审查、合并,再进行下一步。这样做的好处是,一旦某一步出了问题,定位范围非常小,而且前一步的成果已经被验证过,不会被后一步的失败连累。

4.2 给代理一份“任务卡”

在向代理描述任务时,光写一句“帮我加个筛选”是不够的。我习惯把任务写成一张结构化的任务卡,包含四个部分:背景、目标、约束、验收标准。

  • 背景:说明这个任务为什么存在,涉及的现有代码在哪里。
  • 目标:用一两句话说明期望的结果,避免歧义。
  • 约束:说明不能改什么、必须遵守哪些规范、边界在哪里。
  • 验收标准:用可验证的方式说明“什么算完成”,比如“新增测试全部通过”“接口返回符合 swagger 定义”。

一个参考模板如下:

【背景】 订单列表页目前在 order.controller.ts 的 getOrders 接口中实现, 只支持分页,不支持按状态筛选。前端需要新增“待支付/已支付/已取消”三个筛选项。 【目标】 为 getOrders 接口增加 status 查询参数,值为 pending | paid | cancelled, 允许为空(为空表示查询全部状态)。 【约束】 - 只修改 src/modules/orders 目录下的文件。 - 禁止修改支付模块和公共配置。 - 参数校验使用已有 zod schema,不要新增校验库。 - 保持现有响应结构不变,只增加筛选逻辑。 【验收标准】 1. 传给 status=pending 时只返回待支付订单。 2. status 为空时返回全部订单,与旧行为一致。 3. status 传入非法值(如 abc)时返回 400。 4. 新增对应单元测试,覆盖上述三种情况。 5. npm run lint 与 npm test 全部通过。

把这份任务卡交给代理,你会发现它的产出质量比“帮我加个筛选”高出一个档次,因为它不再需要猜测你的意图,所有关键决策你已经替它做了。

4.3 一次完整的执行循环

任务卡准备好之后,完整的执行循环应该是这样的:

第一步,让代理先“读”再“改”。在任务卡里明确要求它先阅读相关文件,甚至让它先输出它对现有代码的理解,确认无误后再动手。这一步能避免大量“答非所问”的改动。

第二步,要求代理分步提交。如果任务确实比较大,让它先输出修改计划,说明打算改哪几个文件、每一步做什么、每步怎么验证。你确认计划之后再让它执行,比让它一口气改完要安全得多。

第三步,代理生成 diff 之后,先由工具检查,再进人工审查。工具检查指 lint、类型检查、测试这些自动化步骤;人工审查则按前面说的审查清单逐项确认。

第四步,审查发现的问题,不要自己动手改,而是把问题反馈给代理,让它自己修正。这样做有两个好处:一是代理能在修正过程中理解规则,后续类似问题会减少;二是你能持续观察它是否真的理解了项目规范,如果多次犯同样的错误,说明你给它的约束还不够明确,需要回到规则文件去补充。

4.4 提交信息也要规范

AI 代理生成的提交信息经常是“fix something”或者“update code”这种毫无信息量的内容。这在做历史回溯时非常痛苦。

我建议在规则文件里明确提交信息格式,比如要求使用 Conventional Commits 规范,并且信息中必须引用对应任务编号或需求单号:

fix(orders): 增加状态筛选参数,支持待支付/已支付/已取消 - 在 getOrders 接口中新增 status 查询参数 - 使用 zod 校验参数合法值 - 补充单元测试 Refs: OMS-1234

提交信息实际上是“代理改了什么”的最小记录。规范之后,即使哪次改动出了问题,翻提交历史也能快速定位到任务卡,大幅降低排查成本。

5. 常见问题与排查思路

AI 编程代理落地过程中有很多反复出现的坑。下面把我在项目中遇到的高频问题整理成一张速查表,并补充每个问题的排查思路。

问题现象常见原因解决思路
改动范围远超任务描述任务边界描述不清晰,代理自由发挥重写任务卡,明确“只修改哪些文件、禁止修改哪些目录”
代码功能正常但风格与项目不一致规则文件中缺少编码规范说明在 AGENTS.md 中补充命名、错误处理、日志规范,并让 CI 强制检查
反复产生重复代码代理没有全局搜索已有工具函数规则中明确“先搜索 shared 目录,再决定是否新增代码”
测试覆盖率高但业务逻辑错误测试只覆盖了代理自己写的路径,缺少对既有行为的回归人工审查时重点看“旧行为是否被破坏”,必要时补充回归用例
代理修改了不该动的历史模块规则文件中没有列出禁止修改的模块把敏感模块明确写进 AGENTS.md 的禁止事项
提交信息无意义,历史无法回溯没有对提交信息做约束在规则中规定 Conventional Commits 格式,并关联任务编号
代理陷入死循环,反复修改仍报错任务描述与代码现状不符,或代理理解偏差停止循环,重新审查任务卡,补充现有代码上下文,必要时人工介入
PR 合并后马上在生产出现异常审查只看功能,忽略了异常分支和容错逻辑审查清单中增加边界条件检查,并要求补关键异常路径测试

下面挑两个高频问题展开说说。

第一个是“改动范围失控”。这几乎是使用 AI 编程代理最容易遇到的问题,根本原因是任务描述里没有“负向约束”。你只说了要做什么,没说不能做什么。解决方案是在任务卡里增加“禁止修改”清单,并且尽量把允许修改的范围缩小到一个文件或一个目录。不要相信代理会自动克制,它默认会采取“最容易实现目标”的方案,而这个方案未必是最小改动。

第二个是“测试看着全绿,实际上没测到点子上”。代理生成的测试有一个通病,就是会针对它自己写的代码路径构造测试,所以测试通过只能说明“代理认为的逻辑是对的”,不能说明“业务的逻辑是对的”。破解方法是审查者用“反向思维”检查测试:先想“旧功能有没有可能被改坏”,再想“有哪些异常输入测试没覆盖”,把这两个方向补充进测试要求里。

6. 工程建议:让 AI 代理的代码长期可控

6.1 先有小步提交的习惯,再谈 AI 提效

很多团队没有引入 AI 代理之前,代码提交就是大块大块地来。这种情况下引入代理,只会让问题更严重,因为代理会继承团队已有的坏习惯,甚至放大它。

安全使用 AI 编程代理的前提,是团队本身已经有小步提交、频繁自测、清晰提交信息的工程习惯。如果你的项目目前还是“一周一次大提交”,那第一步不是引入代理,而是先把提交粒度降下来。代理只是一个放大镜,它放大的是你已有的工程能力。

6.2 让代理在测试的约束下工作

测试不仅是质量保障,也是给 AI 代理的“导航仪”。代理在改代码时,如果能看到相关模块的测试,它就更不容易跑偏。

实际操作中,我习惯在任务卡里把相关测试文件路径直接列出来,让代理在修改前先读测试。要求它保证新改动不破坏已有测试,同时为新增逻辑补充测试。这样做还有一个额外好处:测试本身就是对“预期行为”最精确的描述,代理读懂了测试,往往比读注释更能理解业务意图。

6.3 权限与安全边界要收窄

AI 编程代理在执行任务时,可能需要访问代码仓库、运行命令、操作文件,甚至调用外部 API。这里必须坚持最小权限原则。

不要让代理拥有生产环境的密钥,不要让代理随意执行数据库变更命令,更不要让代理接触到不必要的用户数据。在团队协作场景里,还应该考虑让代理在独立的沙箱环境里运行,与主仓库隔离,合并前必须经过严格审查。安全这一点没有妥协空间,宁可牺牲一点效率,也不能让一个不可控的自动化程序拥有过大的破坏能力。

6.4 定期评估“代理到底省了多少事”

不要凭感觉判断代理是否有效。我建议团队每两周做一次简单复盘,重点看三个指标:合并的代理生成代码占总量比例、代理生成的 PR 被打回率、因代理代码引入线上问题的次数。

打回率尤其值得关注。如果代理生成的 PR 频繁被拒,说明不是代理不行,而是你给它的约束还没建立起来。这时候优先补充规则文件,而不是批评代理。用数据说话,持续迭代约束,团队的 AI 编程代理会越用越顺手。

7. 怎么迈出第一步

如果你准备在团队里安全地引入 AI 编程代理,我给出一条最小可执行的起步路径。

第一步,选一个低风险项目试用,不要一上来就动核心业务。第二步,花半小时写好AGENTS.md,把目录结构、编码规范、禁止事项列清楚。第三步,选一个边界清晰的小需求,用任务卡的方式交给代理执行。第四步,认真完成一次代码审查,把审查意见和项目规则做对比,看看哪些问题其实可以提前通过规则规避,回头补进规则文件。第五步,重复这个过程,直到你发现规则文件开始稳定、需要频繁修改的次数变少,再扩大使用范围。

在这个过程中,你把规则文件逐渐补充完整,就是在把自己对于代码质量的隐性理解沉淀成团队资产。以后不只 AI 代理会参照它,新入职的同学同样可以靠它快速上手项目规范。到这一步,AI 编程代理就不再是一个“需要盯着的风险源”,而是团队工程体系里的一个正常协作角色。

AI 编程代理不会消失,只会越来越强。与其等它推着团队走,不如现在就把约束、审查、反馈这套机制建立起来。写垃圾代码的从来不是工具,而是缺少边界的流程。这套流程建好了,AI 写得越快,团队受益就越大。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 5:37:52

GPT-SoVITS 教程:1 分钟录音克隆音色,原生 48k 合成

GPT-SoVITS 教程:1 分钟录音克隆音色,原生 48k 合成 【免费下载链接】GPT-SoVITS 1 min voice data can also be used to train a good TTS model! (few shot voice cloning) 项目地址: https://gitcode.com/GitHub_Trending/gp/GPT-SoVITS GPT-S…

作者头像 李华
网站建设 2026/9/7 5:37:44

智能汽车芯片选型实战:从技术、量产到口碑的三维判断框架

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

作者头像 李华
网站建设 2026/9/7 5:37:37

北京市环路矢量面数据:构建、质检与空间分析实战

简介:一套覆盖北京二环至六环的环路矢量面数据,基于2020年全国道路数据进行提取、拓扑检查与规整后生成,坐标系统一为WGS_1984_UTM_Zone_51N。该数据面向GIS制图、城市规划、道路可达性分析、行政区划展示等应用场景,二环至六环各…

作者头像 李华
网站建设 2026/9/7 5:36:35

迷你小模型崛起:从GitHub热榜到本地部署的实践指南

每天刷一遍 GitHub Trending 已经成为我这几年雷打不动的固定动作,今天的榜单给我一种很久没有过的兴奋感——霸榜的不是动辄几百 B 参数的“巨无霸”,而是一大批“迷你小模型”。这里的“迷你”并不是说能力缩水到只能当玩具,而是指那些能在…

作者头像 李华
网站建设 2026/9/7 5:36:19

Kinodynamic RRT*:融合动力学约束的机器人运动规划算法解析

简介:这份 Kinodynamic RRT* 算法的 MATLAB 实现,面向机器人路径规划研究者和爱好者,解决在几何与动力学约束下搜索可行最优路径的问题。资源将 RRT* 的渐进最优性与动力学模型相结合,覆盖状态空间表示、距离函数设计、随机采样、…

作者头像 李华
网站建设 2026/9/7 5:35:56

腾讯云上构建生产级Agent:AI Skills与工程化落地全攻略

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

作者头像 李华