news 2026/9/7 3:40:43

告别AI编程助手失忆:跨Session上下文管理与知识沉淀实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
告别AI编程助手失忆:跨Session上下文管理与知识沉淀实战

1. 为什么跨 Session 上下文管理成了 AI 编程助理的头号痛点

1.1 一个典型场景:上下文断裂导致的"失忆"问题

你大概率经历过这个场景:在 IDE 里开了一个长对话,给 AI 助理讲了一上午需求,把模块划分、接口约定、技术栈取舍都交代清楚了,AI 表现也正常,代码生成得又快又对。下午你开了个新 Session 想换个任务接着干,结果它把上午聊的架构决策忘得一干二净,甚至开始提一个和上午结论完全冲突的方案。

更让人崩溃的是,你在新 Session 里重新描述了一遍需求,以为讲清楚了,结果它写出来的代码风格和上午完全不一致,命名习惯变了,错误处理方式也变了。这时候你才意识到:Session 之间的上下文断裂,不仅仅是"AI 忘事"这么简单,它意味着你上午花两小时建立起来的"协作共识"全部作废了。

我在实际使用 AI 编程助手的头几个月,几乎每天都在这个坑里打转。后来我统计了一下,一个 40 人左右的研发团队,平均每个人每天要花 15 到 30 分钟跟 AI 重复已交代过的背景信息,这个时间成本算下来相当吓人。核心问题就一句话:Session 是 AI 的记忆边界,但这个边界跟人类的协作需求是对不上的。

1.2 Session、Context Window 与状态持久化的基本边界

先把这个概念理清楚。所谓 Session(会话),是 AI 交互系统维持连续性的一组状态集合。在聊天机器人、终端工具、IDE 插件里,Session 通常包含会话 ID、消息历史、临时变量、当前工作目录等数据。它的生命周期有严格的边界——会话结束,历史消息可能被清空,或者被归档但不再参与 AI 的推理过程。

而 Context Window(上下文窗口)是更硬性的限制。无论底层用的是什么模型,能一次性输入给模型的 Token 数都是有限的。GPT 类的模型有 128K 甚至 200K 的窗口,但实际业务中要留出输出空间,能用的输入窗口可能只有 60% 到 70%。一旦对话历史加上项目信息超过了这个上限,就必须做截断、摘要或丢弃——这个过程基本不可逆。

Session 管理要解决的问题,就是在 Context Window 有上限、Session 会结束、状态会丢失这三重约束下,依然把"关键上下文"保下来、传下去。注意我说的是"关键上下文",不是"全部上下文"。很多人以为跨 Session 就是要做无损的全量保存,这个想法在工程上是走不通的。全量保存意味着每次开新会话都要把几万字的历史塞进提示词,先不说 Token 成本和费用,光是信息密度下降导致的"重点淹没"问题,就足以让 AI 的表现大打折扣。

1.3 现有方案的局限:为什么复制粘贴不够用

市面上的主流方案,无非是这几类:

第一类,手动复制粘贴。把历史对话中重要的结论复制进新 Session。这个方案的问题是:复制什么、不复制什么,取决于你对 AI 推理机制的理解深度,而且对话越长,有效信息越多,"摘抄遗漏"的概率就越大。我见过很多资深开发者也会漏掉关键的技术约束,比如某次讨论中定了"这个模块不允许引入额外的 ORM 依赖",结果新 Session 里 AI 又给你引入了一个重量级框架。

第二类,系统级会话持久化。有些工具会把会话历史自动保存,支持跨设备续聊。这能解决"历史可回溯"的问题,但不能解决"历史随新会话参与推理"的问题。因为每次调用模型时,系统仍然只把当前 Session 的消息作为上下文,历史归档不会自动注入提示词。

第三类,自定义系统提示词。把项目背景、技术规范写死在系统 Prompt 里。这个方案的问题是静态的,项目演进过程中产生的动态决策(比如"第三版方案弃用了 Redis,改用本地缓存")无法自动同步到提示词中。

理想的做法是:某一时刻,把当前 Session 中真正重要的上下文提炼成一份结构化"交接单",然后通过一个明确的动作传递出去。这个动作,就是我后面要重点讲的 /handoff 和 /teach。它们不是复制粘贴的替代品,而是把"语义提炼"和"知识沉淀"从人工负担变成流程自动化。

2. /handoff 的设计逻辑:把会话上下文当"接力棒"传递

2.1 从"拷贝对话记录"到"移交上下文":设计思路的转变

我第一次实现 /handoff 这个命令时,第一反应是"把历史对话全部导出,然后在新会话里让 AI 加载这个文件"。试了几次后我发现这条路根本走不通——原因很简单,对话记录是线性叙事,而上下文是需要结构的。

对话记录里包含大量寒暄、澄清、试错过程。AI 读取这些记录时要花大量精力去区分"哪些是最终决策""哪些是已被推翻的中间方案"。比如你上午让 AI 用 React 写了一个组件,后来发现性能不行,改成用原生 JS 重写了。如果直接导历史记录,AI 可能会把 "React 实现" 和 "原生 JS 实现" 都当作有效信息,导致它在新 Session 里给你生成第三套完全不同的方案。

/handoff 的核心理念是:从对话中抽取"当前状态"和"决策记录",而不是传递"完整 transcript"。就像足球比赛中的接力棒,你交接给下一棒的不是整个跑道,而是一根实体的棒子和当前的领先位置。

2.2 handoff 的上下文结构化:角色、目标、约束、进度

我践行的 handoff 上下文结构包含六个字段,每个字段都有明确的语义:

字段作用示例
role定义 AI 在当前任务的角色资深后端工程师,熟悉 Kotlin 与 Spring WebFlux
objective当前会话的核心目标完成用户订单模块的异步化改造
constraints不可违反的约束条件不引入新的消息队列,必须兼容 MySQL 8.0
progress已完成事项订单实体改完,DTO 层完成 80%
decisions关键决策及其理由放弃 RabbitMQ,改用本地内存队列,因为日均单量不足 5 万
next_steps下一步明确动作编译通过后处理事务边界,然后补充集成测试

这六个字段覆盖了"Why—What—How—Where"四个维度,刚好对应 AI 在新 Session 中推理新任务时所需的全部前提信息。decisions字段是我特别强调的,因为它记录的是"为什么这样做",而不只是"做了什么"。很多上下文管理方案把注意力放在进度和待办上,忽略了决策背景,结果 AI 经常重复提出已经被否定的方案。

2.3 落地实现:一个最小可用的 handoff 协议

在具体实现上,我倾向于把 handoff 的内容生成一份 Markdown 文件,然后在新的 Session 中通过文件加载。这样做的好处是,文件本身可审查、可修改、可版本管理——你在交给 AI 之前可以自己先看一眼,避免把错误的上下文传过去。

下面是我实际在用的一个 handoff 文件示例,你可以直接抄走:

# Handoff: 用户订单模块异步化改造 ## Role 资深后端工程师,熟悉 Kotlin、Spring WebFlux、MySQL。 ## Objective 完成订单模块的异步化改造,接口响应时间 P95 低于 300ms。 ## Constraints - 不引入新的消息队列组件 - 必须兼容 MySQL 8.0 - 不允许改变现有 REST API 的请求/响应结构 ## Progress - [x] 订单实体与 DTO 改造 - [x] 服务层核心链路改为异步 - [ ] 事务边界处理 - [ ] 集成测试补全 ## Decisions - 放弃 RabbitMQ,改用内存队列,当前日均订单量不足 5 万,无需引入 MQ - 事务控制在 `OrderProcessor` 内部完成,不跨异步边界 - 缓存采用 Caffeine 而非 Redis,减少运维依赖 ## Next Steps 1. 处理事务边界问题,确保失败回滚 2. 补全订单创建、取消的超时场景集成测试 3. 压测验证 P95 指标

这个文件一次丢给 AI,它的临场理解能力比看 20 轮历史对话强得多。有一个细节值得注意:文件里我用了"检查框 + 具体动词"来描述 Progress,这比"部分完成""基本完成"这类模糊表述更利于 AI 判断任务的真实状态。

2.4 handoff 的触发时机与使用姿势

/handoff 不是每个 Session 结束都要执行,滥用会让上下文管理变成额外负担。我总结的三个触发时机是:

  • 上下文窗口警告:当对话开始出现"最早的几条消息被系统截断"的提示时,立刻执行 /handoff。别等到截断发生后再补救,那部分信息已经丢失了。
  • 跨任务切换:同一个项目,从"写功能"切换到"修 bug"或"重构"时,用 /handoff 把功能开发的上下文保存好。
  • 长时间中断:午休、跨天、休假回来后,与其重新翻聊天记录,不如直接生成一份 handoff 文件。

使用姿势上,我推荐"主动审查 + 手动补充"。命令自动生成的东西虽快,但缺少人的判断。AI 可能把一些你以为理所当然但实际很关键的约束忽略了,比如"部署环境只有 2GB 内存"这类信息通常不在对话里明说,但确实会影响技术选型。我在每次 /handoff 过后,都会花 30 秒检查一遍文件,把缺失的背景补上。

3. /teach 的本质:让 AI 从一次会话中提炼可复用知识

3.1 会话结束不等于知识沉淀:teach 解决了什么

/handoff 解决的是"当前任务跨 Session 交接"的问题,但它有一个明显短板——它针对的是"一个任务、一次交接"。如果每个周五你都要跟 AI 聊一遍"代码风格:函数名用 camelCase、DTO 不直接暴露给 Controller、异常统一走全局 handler",那说明这些规则没有被沉淀。

这个场景就是 /teach 要解决的:从一次具体的会话中提炼出可复用的规则、偏好、约束,把它们保存成长期记忆,后续所有 Session 都能加载。

剥开来看,/teach 和 /handoff 的定位差异非常清楚。handoff 是"当前任务的状态快照",带有强烈的临时性——任务完成,handoff 文件基本就失去价值了。而 teach 是"知识提炼",它的产出物应该跨越任务的边界,成为团队或个人的长期资产。用句话概括:handoff 传的是"在这件事上我们讲到哪了",teach 传的是"在这个项目里我们一般怎么做"。

3.2 teach 的提炼策略:从对话中抽取规则、偏好与约束

那 /teach 具体怎么提炼?你不能让 AI 把整个对话总结成一段话然后存起来,那样存进去的是描述性文字,不是可执行的规则。我经过多轮实际使用后,把提炼出的知识分成四种类型,每种的存储方式和表达形式都不同:

第一类:代码风格偏好这类知识最容易被 AI 识别,也最好存储。典型表达是"本项目的 DTO 使用 Java Record 而不是 Lombok 的 @Data""所有 public 方法都必须写 Javadoc"。存储时直接转成祈使句规则,不要带解释,因为解释会稀释规则的强度。

第二类:技术选型决策
这类知识需要带上下文,否则未来会被错误复用。我踩过一个坑:某个项目排除掉了 Redis,原因是团队没人熟悉运维——这个决策在团队扩充后本来应该重新评估,但因为被 AI 当成了"固定规则",后续的 Session 都在刻意绕开 Redis。所以技术选型类知识,必须存成"条件 + 决策"形式:"当部署环境内存小于 1GB 时,不使用 Redis,使用本地缓存"。这样 AI 遇到类似场景时可以判断条件是否满足,而不是盲目套用。

第三类:项目特有的约定比如目录结构约定、命名规约、分支策略。这类规则通常散落在代码 review 的讨论中,很少成文。teach 把它们捞出来变成可加载的规则,价值很高。

第四类:工作流偏好用户喜欢"先给方案再写代码"还是"直接生成完整实现",喜欢"每轮对话给出 3 个选项"还是"直接给最优解"。这类偏好会影响 AI 的交互风格,存储时不能太生硬,否则 AI 会像个机械的客服。

3.3 知识存储与加载:rules 文件、memory 目录与向量索引

存储方式我没有用高大上的向量数据库,而是回归到最朴素的方案——分层文件目录。

project_memory/ ├── rules/ │ ├── code_style.md │ ├── tech_decisions.md │ └── project_conventions.md ├── preferences/ │ └── interaction_style.md └── index.md

index.md是总索引,用 100 字以内的摘要描述每个子文件的主题,这样 AI 在加载时可以先读索引,判断哪个文件与当前任务相关,避免把所有规则一次性全部注入提示词。

加载策略上,我建议分两层:

  • 全局加载rules/code_style.mdpreferences/interaction_style.md这类通用规则,每个 Session 开始时就注入系统提示词。
  • 按需加载rules/tech_decisions.md这类偏业务决策的文件,通过关键词匹配或用户手动指定来加载。

为什么不能全部塞进去?还是上下文窗口的问题。规则文件一多,全部加载会占用大量 Token,而且互相之间可能产生矛盾,比如code_style.md要求 DTO 用 Record,tech_decisions.md里又有一条"历史遗留模块仍用 Lombok,不要改写"。两者同时加载需要 AI 有很大的判断力去区分适用范围,这超出了很多场景下的可靠性要求。

3.4 和 RAG 的区别:teach 不是检索,是提炼

有人可能会问:这不就是 RAG(检索增强生成)吗?把项目资料丢进向量库,需要时检索相关片段喂给大模型,不也能解决"AI 忘事"的问题吗?

区别在于信息的组织方式。RAG 的底层逻辑是"把知识库切成块,检索到相关的块再拼接给模型",它的优势是覆盖面广,适合处理大量非结构化文档,比如源码、wiki、需求文档。但它有一个天然缺陷:检索到的内容可能是原始描述,没有经过"规则化提炼"。你检索到一句"这里曾有人讨论过要不要用 Redis",AI 无法直接判断这是一个已经形成的决策还是悬而未决的议题。

而 /teach 的产出是"明确的指令性规则",它已经是提炼完成的知识,直接注入提示词即可生效,不需要模型再"理解上下文、推断意图"。一个是给你原材料让你自己炒菜,一个是直接给你做好的一盘菜。在实际项目中,RAG 适合搜资料,teach 更适合沉淀协作规则。两者可以共存,但定位完全不同。

4. 实战中的关键决策:工具选型与上下文结构设计

4.1 实现 /handoff 与 /teach 的工具形态对比

动手实现之前,要先决定工具形态。这个决策会影响整个方案的复杂度和日常使用成本。我对比过三种实现路径:

形态优点缺点适用场景
纯命令式(通过 slash command 手动触发)用户可控、实现简单、上下文干净需要用户主动执行,容易忘记个人使用、任务边界清晰
自动关联(到达阈值自动生成 handoff)无需主动操作,不会遗漏可能打断心流,生成质量不稳定长任务、开发者不擅长主动管理
双轨制(自动触发 + 手动 teach)兼顾覆盖率和知识沉淀实现复杂度最高,需维护额外状态团队协作、长期项目

我个人的建议是不要一上来就做双轨制。先用最简单的命令式跑两周,把 handoff 和 teach 的产出物格式打磨好,再考虑加自动化触发。因为自动化的前提是"命令的输入输出你已经足够了解",否则就是为 bug 铺路。

4.2 上下文结构设计的字段选择与权衡

无论如何设计结构,有几个字段层面上的权衡是绕不开的。

第一个权衡:上下文该偏"指令"还是偏"数据"。如果 handoff 文件里全是"你要注意这个""你要记住那个",AI 会倾向于机械执行而缺乏灵活调整的空间。反过来,如果全是数据(进度列表、文件清单、测试日志),AI 又不清楚你希望它以什么姿态去处理这些数据。我的经验是五五开:一半是明确的指令性约束,一半是客观的进度数据。

第二个权衡:decisions 记录要详细到什么程度。太详细会让文件变得冗长,AI 检索关键信息变慢;太简略又会丢失决策背后的环境约束。我的标准是"能让自己在两周后看懂为什么选 A 而不选 B 就行"。一句"用缓存是因为读多写少"不够,至少要写清"当前 QPS 约 2000,读占比 95%,其中最热的数据 10 分钟更新一次"。

第三个权衡:要不要把代码片段存进 handoff。这是一个常见的诱惑——把核心实现代码贴进文件里,想着"这样 AI 就知道了"。但我的实测结论是:不要贴完整代码。原因有二,一是代码本身占 Token 且不可压缩,二是 AI 读代码时会过度模仿局部实现,反而忽略了模块的整体意图。如果你一定要让 AI 了解当前代码状态,贴文件路径加上 3 到 5 行的"核心设计摘要"就够了。

4.3 上下文过大时的压缩策略与信息折损

在长期项目中,哪怕按照上面的字段结构来写,handoff 文件也会变得越来越大。压缩策略我试过三种:

  • 滚桶式压缩:每次生成新 handoff 时,把上一份文件中的progressnext_steps合并成摘要,只保留最近两轮的完整版本。这个策略实现简单,但时间一长会丢失早期决策细节。
  • 按主题拆分子文件:把大型 handoff 按模块或按领域拆分,比如order_module.mdpayment_module.md,新 Session 只加载相关模块。这个方案维护成本更高,但信息密度最好。
  • 摘要 + 原文双层结构:主文件只保留两段话的摘要,细节放在附件中,由 AI 在必要时自己读取附件。效果不错,但依赖插件或工具链对附件的支持。

我在实际项目中采用的是第二种,按模块拆分。这跟代码库的组织方式天然对齐,handoff 文件本质上就是"AI 视角的模块说明书"。

5. 踩坑实录:我在跨 Session 上下文管理中遇到的问题

5.1 上下文污染的连锁反应

第一个坑是上下文污染。某个 Session 里,我让 AI 同时处理两个任务:A 任务是把订单模块改造成异步,B 任务是顺手优化用户查询接口。A 任务已经进入收尾阶段,B 任务刚开始。我在做 handoff 时,把两个任务的信息混在一起写进了一份文件,结果新 Session 里的 AI 严重偏科——它把 B 任务中"优化查询接口"的讨论解读成了 A 任务的一部分,给订单模块加上了几个完全不相干的查询索引。

排查了半天我才发现,问题出在 handoff 文件里这两个任务没有明确的边界标识。从那之后我给自己定了一条规矩:**一个 handoff 文件只对应一个目标,如果 Session 里有多个并行任务,先拆文件再交接。**这是上下文管理中性价比最高的规则。

5.2 过时上下文导致的"固执的错误"

第二个坑比第一个隐蔽得多。有一次,我在 handoff 文件里写了一条决策:"订单状态流转使用状态机模式,状态定义在OrderStatus枚举中。"两周后,另外一个开发兄弟把状态机重构掉了,改用事件驱动的状态流转方案。但我不知道,还在新 Session 里继续让 AI 基于旧的状态机封装扩展逻辑。

AI 严格按照 handoff 文件里的旧状态定义写代码,提交之后代码 review 直接被拒。这个坑的本质是:**上下文文件如果没有随着项目演进而更新,反而会成为错误引导的来源。**从那以后我强制自己在 handoff 文件头部加了一个"最后更新时间"字段,并且在decisions里写清楚"这个决策的适用前提",一旦前提变化,就需要主动更新或废弃对应的上下文文件。这个动作看起来很小,但能避免大量的无效返工。

5.3 压缩丢失关键约束的真实案例

我在做上下文压缩时也翻过车。当时一个 handoff 文件已经积累了三个月的内容,接近 12000 个 Token,我决定用"摘要 + 原文"的双层结构压缩一下,把细节全部挪到附件里。结果新 Session 的 AI 只读了摘要,没读附件(因为我的实现里读取附件的动作不是强制的),于是它完全忽略了"订单模块不允许使用分布式事务"这条硬约束,直接生成了一套基于 Seata 的方案。

复盘下来,这不仅是 AI 的问题,也是我压缩策略的失误。摘要固然要保留核心约束,但"不允许用什么技术"这类否决性规则,必须同时出现在摘要和原文两个层级中,确保任何一层被加载都不会漏掉。这个经验我现在还在用:压缩时优先保留"否定性约束",而非"肯定性描述"——因为 AI 宁可多做,也很少少做,但它最怕的是做了不该做的事。

5.4 多会话并行时的冲突处理

最后一个坑来自多会话并行。当时我在同一个项目里开了三个 Session:一个处理登录模块改造,一个处理订单模块重构,还有一个在处理数据迁移脚本。三条线各自进行,各自都生成了 handoff 文件。但我忽略了它们之间有一个交集——登录模块和订单模块都会修改用户表中的同一个字段。

当我把三个 handoff 文件按模块加载到同一个新 Session 里时,AI 发现了冲突:登录模块要加last_login_at字段,订单模块要加preferred_address_id字段,两个字段都要放在用户表。AI 非常困惑,反复问我"这个表的手工迁移脚本到底以哪个模块为准"。最后我得手动给 AI 讲清楚这两个模块之间的依赖关系。这个经历让我明白了:多 session 并行时,handoff 不仅要写"我做了什么",还要写"我动了哪些共享资源"。共享资源的变更记录,必须集中管理,不能散落在各个模块的 handoff 文件里。

6. 进阶玩法与适用范围

6.1 从辅助学习扩展到代码审查、项目 onboarding

/handoff 和 /teach 的使用场景远不止辅助编程。把一个项目的 handoff 文件积攒到一定数量后,发现它实际上构成了一套"项目活文档"——它比 README 更贴近当前代码的真实状态,比 Confluence 更新得更及时。

我就是靠这套活文档给团队新成员做 onboarding 的。传统方式要花一周读代码加问问题,现在直接让新人加载项目最核心的几份 handoff 文件和 rules 文件,再配合代码浏览,通常两天就能上手。因为 handoff 文件里沉淀的不仅是代码结构,还有"为什么要这么写"的决策记录,这正是新人从一个文件里很难读出来的东西。

6.2 和自动化工作流的结合

另一个有价值的扩展方向是配合 CI。我现在的项目里跑了一个脚本,每日定时扫描最近 24 小时的 Git 提交记录,自动把它们对应的 handoff 文件标记为"可能有变更",触发人工检查是否需要更新决策记录。这个自动化不能完全替代人工,但能在项目快速迭代时帮你及时察觉到上下文文件的"过期风险"。

如果你的团队在跑 AI 代码审查,可以考虑把项目级的 rules 文件作为审查基准注入审查 Agent,而不是放在开发者个人的 Session 里。效果很好——AI 审查时的判断标准和开发者写代码时的约束对齐了,两者不再各说各话。

6.3 我目前的工作流建议

最后分享一个我目前的工作流,你可以根据自己的情况做加减法:

  • 每个项目建一个memory/目录,存放 rules 和 handoff 子文件;
  • Session 开始前,先查看memory/index.md,确认本 Session 要加载哪些上下文;
  • 任务出现跨 Session 需求时,生成结构化 handoff 文件,手动补充关键约束后归档;
  • 发现新的代码风格、技术选型、项目约定时,执行一次 teach 操作,将提炼的规则追加到对应的规则文件;
  • 每两周检查一次 handoff 文件和当前代码是否一致,不一致就更新。

这套流程跑下来,最明显的收益不是"AI 变得更强了",而是"你和 AI 的协作节奏变得稳定了"。它把不可控的"AI 记性好坏"问题,变成了"上下文工程质量"问题——后者至少是我们可以通过流程和工具去干预的。

如果你刚开始尝试,我的建议是:别急着设计一套宏大的上下文管理系统,先挑一个高频动作(比如每次跨 Session 前的交接),用 /handoff 的思路把它做成一份"没那么完美但能用"的交接单。跑两周,迭代几次格式模板,你自然会发现哪些字段有用、哪些字段是冗余的。踩过这几轮坑之后,你会对"上下文管理"这件事有完全不同的理解——它本质上不是技术问题,而是组织问题。

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

OpenAI 回应‘维基事件’:将改进 AI 模型攻击报告方式,呼吁社区定标准

OpenAI 智能体‘维基事件’时间线回溯周六上午,OpenAI 在 X 平台上对‘维基事件’做出回应。自周五首次报道该事件以来,这是 OpenAI 首次承认与此事有关。目前事件全貌和影响范围尚不清楚,但有报道称一群来自 OpenAI 内部的智能体控制了一个德…

作者头像 李华
网站建设 2026/9/7 3:39:43

低空经济赋能农业植保:数字化融合方案的设计与实施

/* 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 3:37:06

从重新加权到重写:训练数据归因如何定位高影响样本并改进模型

/* 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 3:34:58

4G智能ETC行车记录仪测试标准编写实战指南

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

作者头像 李华