日志诊断 Skill 实战:用 ELK 秒级定位根因并生成修复建议
1. 凌晨告警响起时日志诊断 Skill 到底能帮你做什么线上故障排查最让人崩溃的不是修不好而是根本不知道从哪下手。告警面板一片红Kibana 里每秒几万条日志滚过去你输入一个关键词翻三页换个关键词再翻三页。半小时过去了用户投诉已经炸了你还在猜是数据库连接池满了还是某个下游接口超时。日志诊断 Skill 要解决的就是这个环节。它不是一个新造的日志工具而是把 ELK 已经存好的日志数据通过一套结构化的诊断流程自动完成「聚类异常 → 关联上下文 → 推理根因 → 生成修复建议」这四步。你不再需要手动 grep而是让 Skill 把最可能的根因和对应的修复动作直接摆在你面前。适合谁用三类人最直接受益一是 SRE 和运维工程师日常被 P0/P1 告警追着跑二是后端开发需要快速判断线上问题是不是自己服务引起的三是技术负责人希望把故障排查经验沉淀成可复用的诊断流程而不是每次靠某个老员工的经验。我试过在测试环境模拟一个典型的支付超时场景订单服务调用支付网关网关返回超时订单服务重试三次后抛异常。传统方式下我需要分别查订单服务日志、网关日志、网络层日志再手动对齐时间戳。而用日志诊断 Skill 配合 ELK 的 ES|QL 聚类从告警触发到拿到根因报告整个过程压到了 40 秒以内。下面我把这套配置和验证流程完整拆开你可以直接在自己的环境里复现。2. 前置准备TaoToken 接入与 ELK 环境确认在开始配置 Skill 之前需要先把模型调用通道和 ELK 查询通道准备好。日志诊断 Skill 的推理环节依赖大模型能力这里我用 TaoToken 作为模型接入层它兼容 OpenAI 风格的接口配置简单适合快速验证。2.1 获取 API Key 与确认 Base URL首先到 TaoToken 控制台创建一个 API Key。地址是 https://taotoken.net/api-keys 登录后点「创建密钥」复制生成的 sk- 开头的字符串。这个 Key 只显示一次建议先存到密码管理器里。Base URL 固定为 https://taotoken.net/api 不要加任何路径后缀。模型 ID 根据你的场景选日志诊断这种需要理解结构化文本和推理的任务建议用 claude-sonnet-4-20250514 或 gpt-4o前者在长上下文和代码理解上更稳。如果你还没注册可以先到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 了解一下注册后新用户有试用额度足够跑通本文的验证流程。2.2 ELK 环境的最低要求本文假设你已经有一个可用的 Elasticsearch 集群版本 8.x 以上因为 ES|QL 的 CATEGORIZE 函数在 8.13 之后才比较稳定。Kibana 能正常访问并且有一个至少包含 trace_id、level、message、timestamp 四个字段的索引。如果你用的是 Elastic Cloud直接开一个 8.14 的部署即可。自建的话单节点 4 核 16G 就能跑通本文的验证生产环境再按日志量级扩容。确认 Elasticsearch 可访问curl -u elastic:your_password -X GET https://your-es-host:9200/_cluster/health?pretty返回 status 为 green 或 yellow 都行yellow 说明副本分片没分配单节点环境正常。2.3 确认索引中有可诊断的日志执行一条简单的 ES|QL 查询确认目标索引有 ERROR 级别日志FROM app-logs-* | WHERE log.level ERROR | STATS count COUNT() BY log.logger | SORT count DESC | LIMIT 10如果返回空结果说明要么索引名不对要么日志级别字段不是 log.level。你需要根据实际映射调整字段名。这一步很关键字段名对不上后面的聚类和推理全是空转。3. 可复制配置Skill 骨架与 ELK 查询联动这一节是核心我会给出一个可以直接复制运行的 Skill 配置骨架包含模型接入参数、ES|QL 聚类查询、以及告警联动配置。你只需要替换掉 ES 地址、认证信息和索引名。3.1 Skill 配置文件JSON 格式创建一个名为 log-diagnosis-skill.json 的文件内容如下{ skill_name: log-diagnosis, version: 1.0.0, model: { base_url: https://taotoken.net/api, api_key: sk-your-taoToken-key, model_id: claude-sonnet-4-20250514, max_tokens: 4096, temperature: 0.2 }, elasticsearch: { hosts: [https://your-es-host:9200], username: elastic, password: your_password, index_pattern: app-logs-*, verify_certs: false }, diagnosis: { time_range_hours: 24, min_cluster_size: 5, max_clusters: 20, sample_logs_per_cluster: 5, severity_threshold: { critical: 100, high: 50, medium: 10 } }, output: { format: markdown, include_fix_suggestion: true, include_sample_logs: true } }几个参数说明temperature 设 0.2 是为了让推理结果更稳定不要让它自由发挥。min_cluster_size 设 5 表示至少出现 5 次的错误模式才纳入诊断避免噪音干扰。max_clusters 限制单次诊断的聚类数量防止一次拉太多导致模型上下文溢出。3.2 ES|QL 聚类查询模板Skill 内部会执行这条查询来获取异常聚类FROM app-logs-* | WHERE log.level ERROR AND timestamp NOW() - 24 hours | STATS count COUNT(), sample VALUES(message, 3) BY category CATEGORIZE(message) | WHERE count 5 | SORT count DESC | LIMIT 20CATEGORIZE 会自动把相似的 message 归到同一组比如「Connection refused to payment-gateway:443」和「Connection refused to payment-gateway:8443」会被归为同一类。VALUES(message, 3) 取每组最多 3 条原始日志作为样本供模型分析。3.3 告警联动配置Kibana Alerting在 Kibana 中创建一个告警规则当 ERROR 日志速率超过阈值时触发 Skill 诊断。进入 Stack Management → Rules and Connectors → Create rule选择 Elasticsearch query 类型{ rule_type: elasticsearch_query, name: high-error-rate-trigger-diagnosis, index: app-logs-*, query: { bool: { must: [ { match: { log.level: ERROR } }, { range: { timestamp: { gte: now-5m } } } ] } }, threshold: 50, interval: 1m, actions: [ { connector_type: webhook, name: trigger-log-diagnosis-skill, url: http://your-skill-service:8080/diagnose, method: POST, body: {\time_range_hours\: 1, \min_cluster_size\: 3} } ] }这条规则的意思是过去 5 分钟内 ERROR 日志超过 50 条就调用 Skill 服务执行一次诊断。interval 设 1 分钟保证告警触发后 Skill 能尽快拿到最新日志。3.4 Skill 核心诊断逻辑Python 实现下面是一个最小可运行的 Skill 实现依赖 elasticsearch 和 openai 两个库import json from elasticsearch import Elasticsearch from openai import OpenAI class LogDiagnosisSkill: def __init__(self, config_path): with open(config_path) as f: cfg json.load(f) self.es Elasticsearch( cfg[elasticsearch][hosts], basic_auth(cfg[elasticsearch][username], cfg[elasticsearch][password]), verify_certscfg[elasticsearch][verify_certs] ) self.llm OpenAI( base_urlcfg[model][base_url], api_keycfg[model][api_key] ) self.cfg cfg def cluster_errors(self, hours24, min_size5): query f FROM {self.cfg[elasticsearch][index_pattern]} | WHERE log.level ERROR AND timestamp NOW() - {hours} hours | STATS count COUNT(), sample VALUES(message, 3) BY category CATEGORIZE(message) | WHERE count {min_size} | SORT count DESC | LIMIT {self.cfg[diagnosis][max_clusters]} resp self.es.esql.query(queryquery, formatjson) return resp[values] def diagnose(self, clusters): prompt self._build_prompt(clusters) resp self.llm.chat.completions.create( modelself.cfg[model][model_id], messages[ {role: system, content: 你是一个资深SRE擅长从日志聚类中定位根因并给出修复建议。输出格式根因、证据、修复步骤、验证方法。}, {role: user, content: prompt} ], temperatureself.cfg[model][temperature], max_tokensself.cfg[model][max_tokens] ) return resp.choices[0].message.content def _build_prompt(self, clusters): lines [以下是过去24小时的ERROR日志聚类结果\n] for row in clusters: count, samples, category row[0], row[1], row[2] lines.append(f模式{category}) lines.append(f出现次数{count}) lines.append(f样本日志{samples}) lines.append(---) lines.append(\n请分析最可能的根因并给出可执行的修复建议。) return \n.join(lines) def run(self): clusters self.cluster_errors( hoursself.cfg[diagnosis][time_range_hours], min_sizeself.cfg[diagnosis][min_cluster_size] ) if not clusters: return 未发现满足条件的异常聚类。 return self.diagnose(clusters) if __name__ __main__: skill LogDiagnosisSkill(log-diagnosis-skill.json) print(skill.run())这段代码的逻辑很直白先从 ES 拉聚类结果拼成 prompt 发给模型模型返回根因和修复建议。你可以把它包装成一个 HTTP 服务接收 Kibana 告警的 webhook 调用。4. 验证请求从告警到根因报告的完整链路配置写完了接下来要验证它真的能跑通。我建议分三步验证先单独测 ES|QL 聚类再测模型调用最后测端到端链路。4.1 验证 ES|QL 聚类是否返回结果在 Kibana Dev Tools 里直接执行第 3.2 节的查询。如果返回类似下面的结果说明聚类正常{ columns: [ {name: count, type: long}, {name: sample, type: keyword}, {name: category, type: keyword} ], values: [ [1247, [TypeError: Cannot read properties of undefined (reading id)], TypeError.?Cannot.?read.?properties.?of.?undefined], [812, [Connection refused to payment-gateway:443], Connection.?refused.?to.?payment-gateway], [156, [Timeout waiting for response from order-service], Timeout.?waiting.?for.?response] ] }如果 values 为空检查时间范围是否覆盖了有 ERROR 日志的时段或者把 min_cluster_size 降到 1 再试。4.2 验证模型调用是否正常单独跑一段 Python 测试模型连通性from openai import OpenAI client OpenAI( base_urlhttps://taotoken.net/api, api_keysk-your-taoToken-key ) resp client.chat.completions.create( modelclaude-sonnet-4-20250514, messages[{role: user, content: 用一句话解释什么是日志聚类。}], max_tokens100 ) print(resp.choices[0].message.content)如果返回正常文本说明 Key 和 Base URL 没问题。如果报 401检查 Key 是否复制完整如果报 model not found检查模型 ID 拼写。4.3 端到端验证模拟一次故障在测试环境制造一个可控的故障。比如停掉一个下游服务的某个实例让上游服务开始报连接错误。等 1 分钟确认 Kibana 告警触发然后观察 Skill 服务的日志输出。预期看到的结果是一份 Markdown 格式的诊断报告包含## 根因 payment-gateway 的 443 端口连接被拒绝导致订单服务重试三次后抛出异常。 ## 证据 - 过去 24 小时内 Connection refused to payment-gateway:443 出现 812 次 - 样本日志显示重试间隔为 1s、2s、4s符合指数退避策略 - 同一时段 payment-gateway 的 Pod 重启次数为 3 ## 修复步骤 1. 检查 payment-gateway 的 Service 端点是否正常注册 2. 确认 443 端口监听进程是否存活 3. 如果是滚动更新导致等待新 Pod 就绪后重试 ## 验证方法 重新触发一笔订单观察日志中是否还有 Connection refused。从告警触发到这份报告生成实测在 40 秒左右。如果你的环境日志量更大聚类查询本身会慢一些但整体控制在 2 分钟内是合理的。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节列出我在配置过程中实际踩过的坑以及对应的排查方法。你遇到问题时可以按这个顺序检查。5.1 401 Unauthorized这是最常见的错误通常出现在模型调用环节。报错信息类似{ error: { message: Invalid API key provided, type: invalid_request_error, code: invalid_api_key } }排查步骤第一确认 api_key 字段的值是完整的 sk- 开头字符串没有多余空格或换行。第二确认 base_url 是 https://taotoken.net/api 不要写成 https://taotoken.net/api/v1 或其他路径。第三如果 Key 是在环境变量里确认没有把变量名当成值传进去。5.2 local proxy failed 或 connection refused这个错误说明 Skill 服务无法连接到 Elasticsearch 或模型接口。报错信息类似elasticsearch.exceptions.ConnectionError: ConnectionError( urllib3.connection.HTTPSConnection object at 0x...: Failed to establish a new connection: [Errno 111] Connection refused )排查步骤第一确认 ES 地址和端口正确用 curl 单独测一下。第二如果 ES 开了 HTTPS 但证书是自签的把 verify_certs 设为 false。第三检查防火墙规则确保 Skill 服务所在机器能访问 ES 的 9200 端口和 TaoToken 的 443 端口。5.3 reading choices 报错这个错误通常长这样AttributeError: NoneType object has no attribute choices或者KeyError: choices原因是模型返回的响应结构不符合预期。排查步骤第一打印完整的 response 对象看实际返回了什么。第二确认模型 ID 是否正确有些模型不支持 chat completions 接口。第三检查 max_tokens 是否设得太小导致返回被截断。第四如果用的是流式输出确认没有把 stream 参数设成 True 却按非流式解析。5.4 OAuth 相关报错如果你在 Kibana 告警配置里用了 OAuth 认证可能会遇到OAuth token request failed: invalid_client排查步骤第一确认 client_id 和 client_secret 正确。第二确认 token endpoint 的 URL 没有多余路径。第三检查 token 是否过期OAuth token 通常有有效期需要配置自动刷新。如果只是内部测试建议先用 basic auth 跑通再换 OAuth。5.5 聚类结果为空ES|QL 查询返回空 values但 Kibana 里明明能看到 ERROR 日志。排查步骤第一确认索引模式匹配app-logs-* 是否真的匹配到了你的索引。第二确认字段名有些日志的级别字段是 level 而不是 log.level。第三确认时间范围NOW() - 24 hours 是否覆盖了日志的实际时间。第四把 min_cluster_size 降到 1看是否有结果。6. 把诊断能力接入你的日常工作流配置跑通之后下一步是让它真正融入日常运维流程。这里给几个实用的接入方式。第一种是 Kibana 告警联动前面已经配置过了。告警触发后自动调用 Skill诊断报告可以写到 Slack 或企业微信机器人。这样你收到告警的同时根因和修复建议已经在了。第二种是 IDE 插件接入。如果你用 VS Code 或 Cursor可以通过 MCP 协议把 Skill 注册成一个工具。在 settings.json 里加{ mcpServers: { log-diagnosis: { command: python, args: [/path/to/log_diagnosis_skill.py], env: { TAOTOKEN_API_KEY: sk-your-key, ES_HOST: https://your-es-host:9200 } } } }配置好后在 IDE 里直接问「帮我诊断一下最近的支付超时问题」Skill 会自动拉日志、聚类、推理把报告返回给你。第三种是命令行快速诊断。把 Skill 包装成一个 CLI 工具python log_diagnosis_skill.py --hours 1 --min-size 3 --output markdown适合在 SSH 登录服务器后快速跑一次不用打开 Kibana。如果你需要长期跑诊断任务或者想把 Skill 集成到 CI/CD 流水线里做发布后自动巡检可以考虑用 Coding Plan 获得更稳定的模型调用额度。具体可以到 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 了解。模型对话调试可以用 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API Key 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后说一个实际经验日志诊断 Skill 的效果高度依赖日志本身的结构化程度。如果你的日志还是纯文本拼接先花一周时间把 trace_id 和 JSON 格式统一了再上 Skill效果会好很多。否则模型拿到一堆非结构化文本聚类质量差根因推理也容易跑偏。先把基础打好再让 Skill 替你完成那 90% 的侦查工作。

相关新闻

Win10 LTSC安装闹钟和时钟应用的完整指南

Win10 LTSC安装闹钟和时钟应用的完整指南

1. 为什么LTSC用户会执着于“找回闹钟和时钟”——这不是功能缺失,而是系统哲学的碰撞Win10 LTSC(Long-Term Servicing Channel)从诞生第一天起,就不是为普通家庭用户设计的。它面向的是工业控制终端、医疗设备后台、ATM机、数字标…

2026/10/1 14:47:58 阅读更多 →
多线程饥饿 vs 死锁:高并发下CPU飙高却无死锁的真相

多线程饥饿 vs 死锁:高并发下CPU飙高却无死锁的真相

1. 这不是“卡住了”,是线程在挨饿——从下载插件卡顿到数据库响应超时,我们真正该警惕的其实是饥饿现象你有没有遇到过这样的情况:Edge浏览器装了多线程下载插件,明明开了8个线程,但某个大文件就是迟迟不提速&#xf…

2026/10/2 17:44:04 阅读更多 →
SSH远程连接Permission denied排查思路与实战指南

SSH远程连接Permission denied排查思路与实战指南

深夜往服务器上部署代码,屏幕冷冰冰地弹出一行Permission denied, please try again.,那一刻血压确实容易上来。输入三次密码全部被拒,检查键盘大小写没毛病,IP 也没拼错,最后折腾半天才发现是云控制台的安全组根本没放…

2026/10/1 14:47:58 阅读更多 →

最新新闻

Cocos Creator做猜成语游戏:轻量跨端2D开发实战

Cocos Creator做猜成语游戏:轻量跨端2D开发实战

简介:本资源是一套基于Cocos Creator 2.3.3开发的完整猜成语游戏项目源码,面向游戏开发初学者与Unity/Cocos转型开发者,解决2D交互类益智游戏从零搭建、UI逻辑联动、本地数据驱动及跨平台发布等核心实践问题。压缩包共2765个文件,…

2026/10/2 21:45:52 阅读更多 →
yolov5人群计数与阈值报警:从Coco预训练到工程落地的完整方案

yolov5人群计数与阈值报警:从Coco预训练到工程落地的完整方案

简介:面向计算机视觉与公共安防场景,这份资源提供基于YOLOv5的实时人群计数与阈值报警实现。采用COCO预训练的person类权重,可对室内外不严重拥堵的画面进行人数统计,并在超过设定阈值时触发报警,适合需要快速部署人群…

2026/10/2 21:45:52 阅读更多 →
DOSBox中DEBUG汇编实战:从数制转换到物理地址计算

DOSBox中DEBUG汇编实战:从数制转换到物理地址计算

简介:本资源是《IBM-PC汇编语言程序设计》(沈美明、温冬婵编著)配套习题的完整参考答案,面向计算机专业本科生、汇编语言初学者及备考人员,旨在解决课后练习无标准解析、核心概念理解困难、手算验证缺乏依据等典型学习…

2026/10/2 21:45:52 阅读更多 →
北邮研一数据库大作业:学生成绩管理系统从选题到验收完整路径

北邮研一数据库大作业:学生成绩管理系统从选题到验收完整路径

简介:这份资源是北邮研一数据库课程大作业的完整详解文档,面向正在修读数据库系统课程、需要完成课程设计的研究生及高年级本科生。内容围绕学生成绩管理系统展开,覆盖需求分析、数据库设计、ER图绘制、逻辑结构设计与建表程序等完整流程&…

2026/10/2 21:45:51 阅读更多 →
校园二手交易网站毕设项目:SSM+Vue全链路实战解析

校园二手交易网站毕设项目:SSM+Vue全链路实战解析

简介:一套完整的校园二手交易网站设计与实现项目包,面向计算机相关专业毕业设计或课程设计使用者,采用 Java SSM Vue 技术栈构建,覆盖前台用户和后台管理员两类角色。系统包含商品浏览、二手发布、收藏下单、订单发货、求购信息…

2026/10/2 21:44:51 阅读更多 →
南山家常客家风味粤菜靠谱商家测评排名

南山家常客家风味粤菜靠谱商家测评排名

行业选择四大踩坑难题在深圳南山桃源街道周边找家常客家风味粤菜,很多人都踩过不少坑。 一是找不着地道口味的店:想吃正宗的手撕盐焗鸡、客家酿豆腐,跑了好几家店要么味道不够醇厚,要么用的食材不新鲜,吃不出老家的烟火…

2026/10/2 21:44:51 阅读更多 →

日新闻

从零搭建AI工程化:模型之外的完整闭环

从零搭建AI工程化:模型之外的完整闭环

先搞清楚一件事:从零开始做 AI 工程化,难的从来不是调模型、写提示词,而是把一套原型 Demo 变成长得像是“正经系统”的东西。你手里可能已经有了能跑通的代码,也可能刚读完一些概念,但真到了要把它变成可维护、可观测…

2026/10/2 0:00:20 阅读更多 →
大模型训练显存估计与混合精度训练实战指南

大模型训练显存估计与混合精度训练实战指南

1. 大模型训练显存估计与混合精度训练详解显存不够用,几乎是每个做大模型训练的人都会撞上的第一堵墙。你可能也经历过:模型代码写完了,数据管道跑通了,满心欢喜地按下训练启动脚本,结果几秒钟后终端弹出一行红字——C…

2026/10/2 0:00:20 阅读更多 →
小样本学习数据集选型指南:27个真正可用的高质量数据集

小样本学习数据集选型指南:27个真正可用的高质量数据集

1. 小样本学习的“弹药库”:为什么你总在找数据集,却总找不到真正能用的? 小样本、数据集——这两个词最近半年在我处理的200多个AI项目咨询里,出现频率排进前三。不是模型调不好,不是代码写不对,而是卡在…

2026/10/2 0:00:20 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/2 10:36:31 阅读更多 →
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/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练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/2 6:09:11 阅读更多 →