1. 先拆解标题ai-engineering-from-scratch 到底在解决什么问题ai-engineering-from-scratch 这个项目名第一眼看上去像个学习仓库的名字但真正动手做的时候第一个要回答的问题不是学什么而是终点在哪里。我最近跟几个带过新人团队的同行聊大家有个高度一致的感受AI 工程这个方向最大的学习障碍从来不是难度而是信息太散、路径太长。今天刷到一篇文章说先啃数学明天一个课程告诉你必须先精通 PyTorch后天又来个视频讲提示词工程才是未来——一个月下来收藏夹堆了几十个链接手上的代码一行没写。这个项目要解决的核心问题就是把AI 工程化这件事拆成一条有先后逻辑、有明确里程碑的路线。它服务的不是要发顶会论文的研究人员而是想把 AI 能力落到真实产品、真实业务里的工程师。换句话说目标不是发明新的模型结构而是能在实际场景里把数据理清楚、把模型训起来、把推理服务稳定跑上线并且出了问题知道去哪一层排查。以我个人的带人经验这条路线必须包含五块Python 编程能力、机器学习基础、深度学习框架实操、数据处理能力、模型部署与运维能力。我刻意没把数学放在第一位。不是数学不重要而是对工程向的人来说一上来就啃线性代数和概率论大概率会在还没见过模型长什么样之前就放弃了。更合理的顺序是先花一两周跑通一个完整的 demo建立AI 系统到底长什么样的整体体感之后再回头补数学——这时候你才知道那些矩阵运算、概率分布到底在解决什么问题。这个项目适合三类人一是刚毕业或还在校的学生想进 AI 行业但不知道从哪下手二是后端、前端、测试等岗位的工程师想内部转岗到 AI 方向三是业务侧的技术负责人不需要手写模型但要能听懂组员在说什么、能判断技术方案的合理性。这三类人的最终深度不一样但前期的路线高度重合都可以从这条从零路径开始。2. 从零起步四条基础能力线怎么拆2.1 Python 与工程基本功的底线Python 是 AI 工程绕不开的入口。我要强调一个反直觉的点你不需要成为 Python 大师再开始学 AI但你需要有一条明确的底线——能用 Python 写出结构清晰的脚本能读懂报错信息会查官方文档。具体来说以下几个点必须过关。数据结构与流程控制列表、字典、集合、元组要随手能用循环和条件判断要熟练这是所有数据处理的基础。函数与模块化把重复逻辑抽成函数学会拆包、默认参数、异常处理这决定了你后面写训练脚本时代码是能复用还是要反复重写。文件操作会用open、pandas读写 CSV/JSON 是日常操作因为 AI 工程里八成时间在摸数据连文件都读不顺后面寸步难行。这里给个建议不要单独花三个月学 Python而是边做小任务边学。比如给自己安排几个和 AI 无关但需要代码的小项目——写一个批量重命名文件的脚本、抓一个网页表格存成 CSV、写个日志分析工具统计关键词频率。这些任务能帮你把 Python 的基础语法内化成肌肉记忆同时不会因为目标离 AI 太远而失去动力。2.2 数学补到够用而不是求甚解很多新手被AI 需要很高的数学门槛这句话劝退。我的观点是工程向的人数学补到看得懂公式、跟得上推导思路就够了不需要追求从公理推到底。说三个用的最多的数学知识点。线性代数核心是理解矩阵乘法的含义——它本质上是批量变换一个输入向量经过权重矩阵 W 得到输出这就是神经网络一层做的事。你不需要手推矩阵分解但要能看懂y Wx b这种写法知道每个变量的形状。概率统计最常用的是分布、期望、方差、条件概率这些概念在损失函数、评估指标、正则化里反复出现。比如 cross-entropy 损失的直觉就是预测分布和真实分布的差异。微积分用到的主要是求导和链式法则因为反向传播就是链式法则的工程实现。我见过太多人卡在我必须先学完三本数学书再开始这是典型的完美主义陷阱。正确姿势是先跑代码碰到不懂的数学概念再回头查带着问题学效率高得多。比如你训练一个模型发现 loss 怎么都不降这时候去查梯度下降为什么失效比干巴巴地背公式要记得牢得多。2.3 机器学习核心概念别被名词唬住机器学习本质上是从数据中找规律的一套方法论。有几个核心概念绕不开。训练集、验证集、测试集这三者的划分决定了你在模型评估上的判断力很多新手就在这一步翻车。训练集用来学参数验证集用来调超参数测试集用来做最终评估——三者不能混一混你的成绩就是虚高。过拟合与欠拟合用生活化的说法过拟合是背答案背得太死换一套题就懵欠拟合是连作业都没写完考试自然不行。损失函数它是衡量模型好坏的一把尺子回归用均方误差分类用交叉熵选错了损失函数的后果是模型训练方向直接跑偏。这一阶段的重点不是背定义而是动手做实验。拿一个公开的小数据集比如手写数字识别训练一个最简单的模型然后故意把训练集和测试集混在一起观察准确率虚高到什么程度——这种体感是看一百篇文章都换不来的。2.4 工程化工具链从第一天就开始用AI 工程和算法研究最大的区别就是工程化要求更高。有几样工具你从第一天就该接触不要等到学会模型之后。版本管理git 不是可选项是你写代码的基础设施。虚拟环境conda或venv必须会用不然不同项目依赖打架的时候你会非常痛苦。Jupyter Notebook适合做探索性分析和跑小实验但不要用它写正式代码正式代码还是用.py文件组织。命令行基础会用终端切目录、跑脚本、看进程、查显存这些技能在训练和部署时会频繁用到。说句经验之谈工具链的掌握程度决定了你后续学习效率的上下限。一个能用 git 管理代码、用 conda 管理环境、用命令行排查问题的人和只会双击.py文件的人在 AI 工程路上的成长速度差距是几倍的。3. 我推荐的实操路线四步走计划3.1 第一步用一个小项目建立全链路感知我坚决主张第一个项目一定要小但要完整跑通全链路。所谓全链路就是从拿到数据开始经过清洗、训练、评估、保存模型、写一个简单的推理接口把整个流程走一遍。你可以用经典的房价预测或者垃圾邮件分类模型用最简单的逻辑回归或者随机森林就行关键是流程完整。具体步骤是这样第一找一个开源数据集比如波士顿房价数据或某类文本分类数据第二用pandas做基础的数据检查看缺失值、分布情况第三划分训练集和测试集第四用scikit-learn训练一个模型输出准确率或误差第五用joblib把模型存下来第六写一个非常简单的函数从外部输入一条数据返回预测结果。这一步的里程碑标准是你能给一个完全不懂代码的朋友演示这个模型是这么用的。能演示出来说明你对整条链路有了整体感知。很多人跳过这一步直接学深度学习和 Transformer结果就是学了一堆孤立的知识点串不起来。3.2 第二步系统过一遍正规军工具有了全链路体感之后第二步就是升级工具链进入深度学习的正规军阶段。这里推荐 PyTorch因为它的调试体验好、社区生态全、工业界用得广而且写起来比早期 TensorFlow 时代顺手得多。这一阶段的核心任务有三件。掌握张量操作理解shape、dtype、索引、广播机制这是所有深度学习代码的基础。学会构建模型会用nn.Module搭一个简单的多层感知机理解forward函数的作用接着搭一个卷积网络理解卷积层、池化层、全连接层的配合。跑通训练循环手动写一遍前向传播 → 算损失 → 反向传播 → 更新参数这个循环再换成optimizer和DataLoader的写法感受一下工程封装带来的便利。我在这一步给新人的忠告是至少手写一次训练循环不要全程依赖库的封装。为什么因为当你把 loss、梯度、权重更新这几个环节亲手写出来你就理解了训练的本质而这一点理解会在你后面遇到模型不收敛这类问题时帮你快速定位问题在哪个环节。3.3 第三步把模型部署成真正能用的服务训练出模型只是开始AI 工程的重点在能让别人用上。这不仅关系到是否合格还直接关系到你能不能在工作中产生可见的产出。把模型包成一个 HTTP 服务这一步很多人是工作之后才补的但我的建议是自学阶段就要做做一次你就知道生产环境和我们开发环境的差距有多大。推荐的做法是用 FastAPI 加uvicorn起一个本地服务加载你训练好的模型权重提供两个接口一个是健康检查接口GET /health一个是推理接口POST /predict。然后写一个客户端脚本发一个请求看返回结果。这里有个容易踩的坑模型输入数据的预处理和后处理必须和训练时保持一致。比如训练时你把文本做过分词和长度截断那推理服务里也必须做同样的操作否则你部署出去的效果会和本地跑的结果对不上这是新手最容易懵的一个点。更进一步的可以做这几件事把模型封装成 ONNX 格式体验一下跨框架部署加一个简单的批量处理逻辑支持传入多条数据用 Docker 打包服务体会环境一致性的价值。这每一步都在帮你从写脚本的人变成做系统的人。3.4 第四步用真实项目打磨作品集前三个步骤是练习第四步是证明。没有作品集的 AI 工程师在求职和技术交流中都很吃亏。我说的作品集不是说你复现了某个开源项目——那是抄作业真正的问题是你能不能用 AI 解决一个别人没解决过的小问题。可以去身边找需求。比如你的朋友开了一个小网店每天要手动回复大量重复咨询你能不能做一个基于规则的自动回复助手大胆的话甚至可以尝试一个简单的检索式问答模型比如你在的社团每周要统计表格能不能写一个用 OCR 提取关键信息的小工具再比如你自己喜欢看某类文章能不能做一个内容分类器把碎片化的文章自动归档整理。作品集的质量标准不是模型准确率有多高而是问题有没有被完整解决、过程有没有文档化。我强烈建议每个项目都写一个 README说清楚要解决什么问题、数据从哪来、怎么处理的、遇到什么坑、最后效果如何。这不仅是给别人看的也是给你自己几个月后回顾用的——你踩过的坑写下来才是真正属于自己的经验沉淀。4. 实操过程里的关键环节与避坑经验4.1 数据永远是第一个瓶颈在 AI 项目里有一句老话模型选得再准也救不了喂进去的烂数据。很多新手第一次做项目拿到数据就直接丢进模型训练结果效果很差然后开始怀疑自己的模型是不是有问题。实际上八成的原因是数据质量不过关。数据处理有几个必须过的关卡。缺值处理是删行、填平均值还是用别的策略取决于数据量和业务含义。重复数据前处理阶段就要去重不然同样的样本既进了训练集又进了测试集评估分数就会虚高。标签分布检查类别分布是否严重不均衡如果正负样本比接近九比一直接训练的话模型往往会变成全预测多数类的懒惰选手这时候需要考虑采样策略。数据泄漏这是最难察觉的一类问题。比如在做时间序列预测时如果数据预处理用了全局的归一化参数等于让模型在训练时就看到了未来的统计信息测试效果虚高。我当时在数据预处理上踩过一个很典型的坑。因为随手用了一个fillna(valuemean)把缺失值全部填成了全局均值看起来没问题但后续做特征工程时反复出现消失的有效信息。后来才发现均值填充会把数据的分布压窄方差减小模型的分辨能力跟着下降。从那以后我会先画个分布图看看填充前后的差异再决定填充策略。4.2 训练的玄学与科学神经网络训练在很多人眼里是玄学但我更愿意说它有玄学成分但大部分现象是有规律可循的。几个高频问题loss 不降、loss 变成 NaN、训练震荡、收敛太慢。每个问题背后通常有几个固定嫌疑。loss 不降的时候我建议按这个顺序排查第一先跑一个小样本过拟合测试取一百条数据看模型能不能把这一百条背下来。如果连小样本都学不动说明模型结构或代码有 bug先别调参。第二检查学习率这是最常出问题的参数。学习率太小更新太慢loss 像蜗牛爬学习率太大loss 直接炸掉或者变成 NaN。第三检查数据预处理特征是不是没归一化。很多模型对输入尺度很敏感大数值特征会把梯度带偏。loss 变成 NaN 的常见原因有学习率太大导致梯度爆炸、除零操作、log 了负数或者数据里本身有异常值。排查方式是加一行断言检查每一层输出是不是有限的数或者用更小的学习率先试。训练震荡通常是学习率偏大或者 batch size 太小可以考虑换优化器或者加入学习率衰减策略。实操来说我的习惯是先用一个小学习率配一个小模型确认能学会了再逐步放大模型规模。别一上来就照搬大模型的配置硬件不够的时候这样只会浪费时间。4.3 部署时最容易翻车的地方部署环节新手最容易踩的坑集中在本地能跑、部署就挂这件事上。本质原因是本地环境和服务环境的依赖不一致。解决办法就是容器化把 Python 版本、依赖库版本、系统底层库全部固化在镜像里。但容器化也有一层坑装的依赖版本如果和本地 dev 环境不一致训练时numpy是 1.x到了容器里升级成 2.x可能会有矩阵运算行为差异概率虽小但踩中了会让你排查到怀疑人生。建议 pip 安装依赖时用requirements.txt锁定精确版本别用这种宽松写法。另一个高频问题是路径硬编码。本地写代码时用的是绝对路径比如C:\Users\xxx\data\train.csv或/Users/xxx/project/model.pth部署到服务器或者 Docker 里路径就完全失效了。正确做法是代码里全部用相对路径或者通过环境变量注入路径。还有一件容易被忽略的事预告处理逻辑一致性。训练时的数据清洗步骤、特征转换步骤推理时必须原样复制。简单说训练和推理吃的是同一道工序的菜你训练时洗了菜、切了丁、炒了炒推理时就不能把整颗菜扔进去。前后处理的代码最好抽象成一个公共函数两边调用同一个这样能从根本避免前后不一致的问题。4.4 资源有限时怎么破局很多自学的人卡在没有好的 GPU。我要说的是不要等到有 GPU 才开始学。前期的数据探索、构建模型、训练小模型、调参逻辑完全可以在 CPU 上完成。PyTorch 的 CPU 版本照样能跑只是慢一点。你完全可以用 MNIST、CIFAR 这类小数据集把整个训练和调优流程先走通。真正需要 GPU 的场景是大模型训练和大规模数据迭代这完全可以放到后期。到那时候有几个思路一是用云平台的按量付费 GPU 实例训练时开几个小时跑完关掉二是用免费的在线 Notebook 环境三是尽量用小模型、小 batch、混合精度训练来降低资源消耗。总之资源问题的最佳解决方案是先把自己的流程能力和调试能力练好——等你真的拿到 GPU 的时候你不会因为不会用而浪费那几美元一小时的时间。5. 常见问题速查与排查思路这块内容我在带新人时整理过一张速查表直接贴出来适合当工具用。遇到问题先对号入座再深入排查。模型不收敛loss 不降排查优先级先跑小样本实验确认代码没 bug → 检查数据是否归一化 → 把学习率调小一个数量级比如从 1e-3 降到 1e-4→ 检查损失函数是否用对 → 看梯度是不是消失了。多数情况是学习率或者数据问题。过拟合与欠拟合过拟合的典型表现训练 loss 持续下降、验证 loss 先降后升。应对手段按优先级排列增加数据量或做数据增强 → 简化模型结构 → 加 dropout 或正则化 → 早停。欠拟合的表现是训练和验证 loss 都很高这时候核心是增加模型的表达能力别急着加正则化。显存不够常见解法顺序减小 batch size → 降低输入图片/序列长度 → 用混合精度训练 → 清理不用的计算图比如推理场景下用torch.no_grad()→ 换个更小的预训练模型。如果这些都不行再考虑分布式或者云资源。效果不稳定每次训练结果差异大主要是随机性在作祟——权重初始化、数据加载顺序、dropout 都引入了随机因素。解决方法是固定随机种子并且在多折验证下评估模型不要拿单次结果下结论。深度学习里做对比实验不固定随机种子结论基本不成立。本地效果好、上线效果差这是数据分布变了或者前后处理不一致。解决方案排查线上和本地的输入数据分布差异、确认预处理逻辑一致、建立监控来追踪线上输入的分布漂移。还有一个容易漏的模型输出到业务逻辑之间的阈值设置训练时用的阈值不一定适合线上需要根据线上数据重新标定。还有一个挺好用的技巧把调试信息打全。训练脚本里的每一次 epoch 都要输出 loss、accuracy、学习率方便你回溯问题出现在哪个阶段。别嫌 log 吵出问题的时候这份 log 就是你排查问题的地图。6. 最后分享几个我反复用到的体会写到这儿我想把一些代码之外的东西分享出来这些东西在文档里很少见到但我觉得对走这条路的人特别重要。第一计划里永远给踩坑预留时间。自学 AI 和上班做项目不同上班有 deadline 逼着你自学的时候很容易因为一个环境问题卡住就放弃了。我的习惯是每设置一个学习目标同时预设 20% 的额外时间专门用来解决莫名其妙的问题——依赖装不上、版本冲突、下载失败。预判了这些意外心态就会从我怎么这么倒霉变成果然来了那就解决吧。第二多写文档尤其写错题本。我自己在项目里会维护一个简单的笔记文档专门记录今天踩了什么坑、根因是什么、怎么解决的。这个习惯刚开始看起来浪费时间但三个月以后你再回头看会发现那些当时觉得天都要塌下来的问题其实半小时就能解决而且大概率还能帮到身边的人。文档化的能力也是 AI 工程师进阶到高级岗位时最难补齐的短板之一。第三代码能力和数学能力比模型更保值。AI 领域的新模型层出不穷今天火的这个框架明天可能就换主流了。但代码能力——能不能把它拆解清楚、改对、调对——还有数据分析能力、系统设计能力这些是不会过时的。所以当你在学新模型和把基础代码能力练扎实之间犹豫时我会建议你选后者。基础扎实的人学新模型只需要一周基础不牢的人学新模型等于一次次从零开始。最后说个实际的建议这个项目从零走到能独立完成一个端到端的 AI 工程任务我个人的经验是四到六个月前提是每天投入两个到三个小时。别信那些三个月带人拿高薪的宣传正常节奏就是半年左右把基础打牢然后找到一个具体方向深耕下去。AI 工程不是一个能速成的领域但它是一个只要路线对、持续投入就一定有回报的领域因为这行的需求实实在在摆在那里缺的从来不是热情而是能把事情完整做出来的人。
企业数字化 ERP 产品动态
相关推荐
Clipper2多边形裁剪:C++、C#、Delphi跨语言落地与调优 多边形裁剪这个词听着像图形学的教科书名词,但真到了工程里,它出现的地方比想象中多得多:排版软件里两个色块叠加要算公共区域,CAD 里把零件轮廓和加工窗口求交,地图渲染时把行政区边界和可视窗口做一次裁切࿰… · 2026/11/2 14:41:56
在线表格嵌入前端:用Univer实现单元格白名单编辑与只读控制 最近接了一个需求,刚开始觉得不太“前端”:网页里放一个在线表格,用户只能往指定的几个单元格里填内容,其他区域比如表头、合计行、审批列,一律不允许改动。这种场景很常见,比如激励申报、预算填报、台账录… · 2026/11/2 14:41:56
Paperclip基准:大模型拼接文档推理与引用一致性评测 1. Paperclip是什么:大模型推理评测的新方向 最近圈子里聊得最多的benchmark,除了传统的MMLU、GPQA这些老面孔,就是OpenAI这个新放出来的Paperclip了。全称是Public Pivotal Reasoning Evaluation of Concatenated Inputs from Language Mode… · 2026/11/2 14:41:56
Wan 2.2 Animate V2:ComfyUI驱动下的姿态视频生成技术革新 Wan 2.2 Animate V2:ComfyUI驱动下的姿态视频生成技术革新
在数字内容创作领域,姿态驱动的视频生成技术正经历着前所未有的突破。Wan 2.2 Animate V2作为ComfyUI平台上的一款先进工作流程,彻底改变了传统动画制作的范式。该工具能够将单张参考… · 2026/11/3 9:42:23
腾讯混元图像3.0开源:800亿参数重构AIGC创作生态 腾讯混元图像3.0开源:800亿参数重构AIGC创作生态
导语:国产大模型首次登顶国际文生图榜单
2025年9月28日,腾讯正式开源混元图像3.0(HunyuanImage 3.0),这款参数规模达800亿的原生多模态模型不仅刷新了开源… · 2026/11/3 9:42:23
Ming-UniVision:革新视觉语言交互范式的连续令牌多模态模型 Ming-UniVision:革新视觉语言交互范式的连续令牌多模态模型
在人工智能领域,多模态大模型的发展正经历着从分离式架构向一体化系统的关键转型。Ming-UniVision作为这一变革的先锋,凭借其基于MingTok构建的连续视觉令牌技术,彻底打… · 2026/11/3 9:42:23
4240亿参数重塑AI产业:ERNIE 4.5多模态大模型的技术突破与行业变革 4240亿参数重塑AI产业:ERNIE 4.5多模态大模型的技术突破与行业变革
导语
百度ERNIE 4.5系列大模型以4240亿总参数的异构混合专家架构实现性能飞跃,通过开源策略加速多模态AI在金融、工业、医疗等领域的规模化落地,重新定义大模型"效率… · 2026/11/3 9:41:23
双极步进电机控制实战: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