大模型推理引擎vLLM(7):Chunked-Prefills 分块预填充机制与 TaoToken 统一 Key 通道实践
1. 从一次线上抖动说起为什么长 Prompt 会拖慢所有人先说一个我真实遇到过的场景。某次内部压测一台 A100 上跑着 vLLM 服务模型是 8B 级别的指令模型。平时短问答的 TBT相邻 token 间隔稳定在 30ms 左右结果一接入文档摘要任务单条 prompt 动辄 6000 到 8000 token整个服务的 TBT 直接飙到 200ms 以上短请求的用户体感就是卡住了。查日志发现问题不在显存也不在模型本身而在调度一个超长 prefill 请求占满了整轮迭代所有正在 decode 的请求都得排队等它算完。这就是 vLLM 里 Chunked-Prefills分块预填充要解决的核心问题。它是什么一句话把一条长 prompt 的 prefill 计算切成若干小块分散到多轮迭代里完成每轮只处理一部分 token并和正在进行的 decode 请求混在同一个 batch 里执行。它能做什么降低首 token 延迟TTFT的抖动、压低 TBT、提升 GPU 整体吞吐。适合谁所有在单卡或多卡上部署 vLLM 做在线推理、并且请求长度分布很不均匀的团队。如果你只是本地跑个 demo短 prompt 为主那默认配置基本够用。但只要你的服务里混着长文档、RAG 拼接上下文、代码补全这类长输入Chunked-Prefills 的调参就直接决定你的服务质量。这篇我会把机制讲清楚然后给出一套可复制的 vLLM 启动参数、分块配置项、请求验证脚本最后用 TaoToken 统一 Key 通道把多模型推理服务接起来方便你在同一套代码里切换模型做对比验证。先明确几个后面会反复用到的术语避免混淆。TTFT 是从发出请求到生成第一个 token 的时间主要反映 prefill 阶段性能。TBT 是连续两个输出 token 之间的间隔反映 decode 速度。TPOT 是所有输出 token 的平均生成时间也叫 ITL。整体延迟约等于 TTFT 加上 TPOT 乘以生成 token 数。Chunked-Prefills 优化的就是 TTFT 和 TBT 之间的那个矛盾prefill 想一次算完吞吐高decode 想尽快出字延迟低两者抢同一块 GPU。传统做法里prefill 是计算密集型矩阵乘矩阵算力利用率高但显存带宽用不满decode 是内存密集型每次只处理一个 token大部分时间花在读取 KV cache 和模型权重上算力大量闲置。把两者混在一个 batch 里prefill 就能搭便车用掉 decode 没吃满的算力decode 也能和 prefill 共享一次权重读取。这就是混合 batch 的收益来源也是 Chunked-Prefills 的立足点。2. Chunked-Prefills 机制拆解与 vLLM 里的关键参数要理解分块预填充得先看它替代了什么。最早的 Static Batching 是凑一批一起跑全跑完才换下一批最慢的请求拖死所有人。后来 Continuous BatchingOrca 提出的 iteration-level scheduling改成每轮迭代动态决定 batch 组成谁生成完 EOS 就立刻换新请求进来GPU 利用率大幅提升。但 Continuous Batching 有个隐患它按先来先服务拼 batch一个 batch 里塞进多少 prefill、多少 decode 完全不受控。如果某轮恰好进来几条长 prompt整个 batch 就变成 compute-bounddecode 请求被硬生生拖慢。Sarathi-Serve 的思路是给每轮迭代的 prefill token 数量设一个上限也就是 token budget在 vLLM 里对应max_num_batched_tokens。调度器优先安排正在进行的 decode每个只吃 1 个 token对延迟最敏感然后用剩余的 token 预算去填 prefill填满一个请求的 chunk 再填下一个预算用完就停。这样每轮迭代的计算量近似均衡decode 的 TBT 几乎不受输入 prompt 总长度影响。这里有个容易搞混的点chunk size 不是把 prompt 切成多大的块而是每轮迭代允许处理的最大 token 总数它包含 decode token 加 prefill token。比如max_num_batched_tokens2048某一轮可能是 3 个 decode 请求各 1 token加上一个 prefill 请求的 2045 个 token合计 2048。下一轮这个 prefill 请求继续吃剩下的部分。所以调这个参数本质是在调每轮迭代的延迟上限。分块带来的开销有两个。一是 chunk 变小后算术强度下降GPU 利用率降低prefill 整体变慢但实测开销通常控制在 1.25 倍以内。二是 Attention 的重复读取每个 chunk 做 Attention 时都要重新读之前所有 chunk 的 KV cache第一个 chunk 的 KV 会被读 N 次。不过 Attention 在整个前向里占比小线性层才是大头所以端到端影响有限。调 chunk size 时有个坑必须避开tile quantization。GPU 矩阵乘法按 tile常见 128分块如果维度刚好超过 tile 整数倍一点点比如从 256 到 257延迟可能从 69.8ms 跳到 92.33ms涨 32%。所以选max_num_batched_tokens时尽量让它和搭载的 decode token 数之和落在 tile size 的整数倍附近比如 2048、4096 这种。在 vLLM v1 里Chunked-Prefills 默认开启。核心调优参数就是max_num_batched_tokens设小如 2048能压低 TBT适合交互式场景设大如 16384能改善 TTFT适合批处理追求极致吞吐时小模型加大显存可以设到 8192 以上。下面这张表是我实测下来比较有用的对照。参数作用偏交互低 TBT偏吞吐高 QPSmax_num_batched_tokens每轮迭代 token 上限20488192~16384max_model_len单请求最大序列长度按业务设别虚高按业务设enable_chunked_prefill是否开启分块预填充默认 True默认 Truegpu_memory_utilization显存占用比例0.85~0.90.9~0.95需要提醒的是max_model_len设太大会白白吃掉 KV cache 显存。比如某些模型默认 131072光 KV cache 就要 16GiB 以上显存不够时 vLLM 会直接报错拒绝启动。按你真实的最长输入来设别图省事。3. 可复制的 vLLM 启动配置与 TaoToken 统一 Key 接入这一节给你能直接抄的配置。先装环境我用 uv 管理虚拟环境干净利落。# 安装 uv curl -LsSf https://astral.sh/uv/install.sh | sh source $HOME/.local/bin/env # 创建虚拟环境 uv venv vllm-demo --python 3.12 --seed source vllm-demo/bin/activate # 安装 vLLM 和 benchmark 依赖 uv pip install vllm pandas datasets启动一个带 Chunked-Prefills 调优的 OpenAI 兼容服务。注意--max-num-batched-tokens是核心--enable-chunked-prefill在 v1 默认开显式写上更清楚。python -m vllm.entrypoints.openai.api_server \ --model deepseek-ai/DeepSeek-R1-Distill-Llama-8B \ --served-model-name ds-r1-8b \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 8192 \ --max-num-batched-tokens 2048 \ --gpu-memory-utilization 0.9 \ --enable-chunked-prefill服务起来后本地是http://127.0.0.1:8000/v1。但如果你要在一个客户端里同时对比多个模型、多个推理后端逐个改 base_url 和 key 很烦。这时候用 TaoToken 的统一 Key 通道就顺手了一个 API Key一套 OpenAI 兼容协议切换模型只改 model 字段。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。下面是一个 Python 客户端配置把本地 vLLM 和 TaoToken 通道放在一起对比。注意 Base URL、Key、Model ID 三件套要写全。import os from openai import OpenAI # 本地 vLLM 服务 local_client OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, # vLLM 本地默认不校验 ) # TaoToken 统一 Key 通道 tao_client OpenAI( base_urlhttps://taotoken.net/api/v1, api_keyos.environ[TAOTOKEN_API_KEY], ) def ask(client, model, prompt, max_tokens256): resp client.chat.completions.create( modelmodel, messages[{role: user, content: prompt}], max_tokensmax_tokens, temperature0.2, ) return resp.choices[0].message.content if __name__ __main__: long_prompt 请总结下面这段技术文档的要点 分块预填充通过限制每轮迭代的 prefill token 数量来均衡计算负载。 * 200 print(本地 vLLM:, ask(local_client, ds-r1-8b, long_prompt)[:120]) print(TaoToken:, ask(tao_client, claude-sonnet-4-5, long_prompt)[:120])如果你用 Claude Code 这类工具做长上下文编码TaoToken 的接入配置可以写成 settings 片段。Base URL 填https://taotoken.net/apiKey 用你的统一 KeyModel ID 按需选。这样同一套配置能在不同模型间切换方便你验证 Chunked-Prefills 在不同模型上的表现差异。{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: 你的_TAOTOKEN_API_KEY, ANTHROPIC_MODEL: claude-sonnet-4-5 } }需要拿 Key 的话去控制台创建https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API Keys 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。4. 验证请求与吞吐/首 token 延迟对比实测配置写完必须验证不然你不知道分块到底有没有生效。先发一个最简单的请求确认服务活着。curl http://127.0.0.1:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: ds-r1-8b, messages: [{role: user, content: 用一句话解释什么是分块预填充}], max_tokens: 64 }返回里能看到choices[0].message.content就说明通了。接下来做吞吐对比用 vLLM 自带的 benchmark 脚本。先下数据集。git clone https://github.com/vllm-project/vllm.git cd vllm/benchmark wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json跑两组对比一组max-num-batched-tokens2048一组 8192其他不变。# 小 chunk偏交互 python benchmark_throughput.py \ --backend vllm \ --model deepseek-ai/DeepSeek-R1-Distill-Llama-8B \ --dataset ShareGPT_V3_unfiltered_cleaned_split.json \ --max-model-len 8192 \ --max-num-batched-tokens 2048 \ --num-prompts 1000 # 大 chunk偏吞吐 python benchmark_throughput.py \ --backend vllm \ --model deepseek-ai/DeepSeek-R1-Distill-Llama-8B \ --dataset ShareGPT_V3_unfiltered_cleaned_split.json \ --max-model-len 8192 \ --max-num-batched-tokens 8192 \ --num-prompts 1000我实测下来2048 那组输出大概是Throughput: 4.85 requests/s, 2007.66 total tokens/s, 962.92 output tokens/s总 prompt token 21 万多输出 token 19 万多。8192 那组总吞吐会更高但如果你同时用脚本测 TBT会发现 2048 的 TBT 明显更稳。这就是权衡吞吐和延迟不可能同时拉满得按业务选。想单独测 TTFT 和 TBT可以写个带时间戳的脚本用流式接口记录首 token 到达时间和相邻 token 间隔。import time from openai import OpenAI client OpenAI(base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY) def measure(prompt, max_tokens128): start time.time() first_token_time None last_time None gaps [] stream client.chat.completions.create( modelds-r1-8b, messages[{role: user, content: prompt}], max_tokensmax_tokens, streamTrue, ) for chunk in stream: if chunk.choices[0].delta.content: now time.time() if first_token_time is None: first_token_time now else: gaps.append(now - last_time) last_time now ttft (first_token_time - start) * 1000 tbt sum(gaps) / len(gaps) * 1000 if gaps else 0 print(fTTFT{ttft:.1f}ms, 平均TBT{tbt:.1f}ms, token数{len(gaps)1}) long_prompt 请详细分析以下内容 分块预填充把长 prompt 拆成小块与 decode 混合调度。 * 300 measure(long_prompt)跑长 prompt 时你会看到 TTFT 因为分块而略微上升要分多轮才 prefill 完但 TBT 保持平稳不会像没开分块那样突然飙高。这正是 Chunked-Prefills 想要的效果牺牲一点点首 token 时间换来整体延迟的稳定。5. 常见报错排查401、local proxy failed 与 reading choices接入和压测过程中报错基本集中在几个地方。我按真实遇到的顺序列一下。401 Unauthorized。用 TaoToken 通道时最常见。先确认 Key 有没有带对前缀再确认 Base URL 是不是https://taotoken.net/api/v1注意结尾的/v1OpenAI SDK 会自己拼/chat/completions别重复。如果本地 vLLM 报 401检查是不是误传了真实 Key本地服务 api_key 填EMPTY即可。还有一种情况是环境变量没生效os.environ[TAOTOKEN_API_KEY]取到空字符串打印一下确认。local proxy failed / connection refused。这个通常是本地 vLLM 服务没起来或者端口被占。先curl http://127.0.0.1:8000/v1/models看服务在不在。如果启动时报显存不足把--gpu-memory-utilization降到 0.85或者调小--max-model-len。注意别把max_model_len设得比模型实际支持还大vLLM 会直接拒绝。reading choices 相关报错比如KeyError: choices或解析响应失败。这多半是返回体不是标准 OpenAI 格式或者请求被中间层拦截返回了 HTML 错误页。先打印原始resp看内容。如果是流式请求注意chunk.choices可能为空比如最后一个 usage chunk要加if chunk.choices判断。用 TaoToken 通道时确认 model 字段填的是平台支持的 Model ID填错会返回错误结构。OAuth / 认证类报错。如果你用 Claude Code 或类似工具报 OAuth 相关错误检查 settings 里的ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY是否成对配置。Base URL 用https://taotoken.net/apiKey 用统一 KeyModel ID 写全。三件套缺一个都会认证失败。改完配置记得重启工具进程很多工具只在启动时读一次环境变量。分块没生效的排查。如果 TBT 还是抖先确认启动日志里有没有 chunked prefill 相关字样再确认max_num_batched_tokens是不是被设得过大等于没分块。另外如果单条 prompt 本身就比max_num_batched_tokens小那它一轮就 prefill 完了自然看不到分块效果得用超长 prompt 测。6. 把统一 Key 通道用起来多模型对比与长期编码调完参数最后一步是让这套东西真正好用。我的做法是本地 vLLM 跑主力模型做高频推理TaoToken 统一 Key 通道负责模型对比和长上下文任务。这样一套客户端代码改个 model 字段就能在本地模型和云端模型之间切换验证 Chunked-Prefills 在不同规模模型上的表现差异。如果你要长期做编码或 Agent 类任务可以考虑 Coding Plan把统一 Key 用在持续性的开发流程里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 。Claude Code 的接入说明在 https://taotoken.net/claudecode?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。回到 Chunked-Prefills 本身我踩过最大的坑是盲目把max_num_batched_tokens往大调以为吞吐越高越好结果交互场景 TBT 抖得没法用。后来固定成 2048 起步再根据压测的 TBT 曲线微调找到那个既不违反延迟目标、又能吃满吞吐的点。另一个坑是忘了 tile 对齐2048 和 4096 这种值比 2000、3000 稳得多。你可以先按 2048 跑一轮 benchmark记录 TTFT 和 TBT再换 4096 跑一轮对比之后基本就有感觉了。

相关新闻

[工具分享] CCSwitch 接入 Claude 无限 token:把 settings 改到 TaoToken 的完整配置

[工具分享] CCSwitch 接入 Claude 无限 token:把 settings 改到 TaoToken 的完整配置

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

2026/10/13 12:06:35 阅读更多 →
AIOps基本包实战:用Python搭建监控异常检测与告警系统

AIOps基本包实战:用Python搭建监控异常检测与告警系统

简介:面向智能运维开发与运维人员的Python工具包资源,聚焦异常检测、告警收敛、时间序列预测与告警关联分析四大核心模块。包内功能以即插即用的模块化方式实现,用户可直接导入对应模块集成到现有监控或运维流程中,适合具备一定Py…

2026/10/13 12:06:38 阅读更多 →
X光安检目标检测数据集落地四步法:VOC转YOLO避坑指南

X光安检目标检测数据集落地四步法:VOC转YOLO避坑指南

简介:本资源是面向计算机视觉初学者与目标检测算法研发者的X光安检场景专用数据集,覆盖打火机、刀具、充电宝等10类违禁物品,专为YOLO、Faster R-CNN等主流检测模型训练与测试提供高质量标注支持。压缩包共2000个文件,含1999份Pas…

2026/10/13 12:06:42 阅读更多 →

最新新闻

Unity水下波光Shader:动态折射与材质自适应光照实现

Unity水下波光Shader:动态折射与材质自适应光照实现

1. 这个效果到底在解决什么问题:水面光影的“动态欺骗”本质很多人看到“Unity Shader实现水中波光照射动物身上的效果”这个标题,第一反应是:“不就是加个高光贴图或者用个Blinn-Phong算一下反射吗?”——这恰恰是踩进第一个认知…

2026/10/13 13:00:12 阅读更多 →
MATLAB水果识别实战:从图像处理到STM32部署

MATLAB水果识别实战:从图像处理到STM32部署

简介:本资源是一个基于MATLAB实现的轻量级水果图像识别程序,面向图像处理初学者、自动化专业学生及农业智能化应用开发者,解决常见水果(如香蕉、梨、西红柿、青椒等)的自动分类识别问题,适用于课程设计、小…

2026/10/13 13:00:12 阅读更多 →
金山毒霸卸载后残留弹窗的根源与彻底清理指南

金山毒霸卸载后残留弹窗的根源与彻底清理指南

卸载金山毒霸之后还在被弹窗骚扰,这事儿太常见了。我见过不少用户重装了系统才解决问题,其实根本不用这么极端。这篇内容就把残留弹窗的来源、手工清理步骤和常见坑一次性说清楚。 先说明白一个核心事实:金山毒霸的卸载程序只会移除主体程序…

2026/10/13 13:00:12 阅读更多 →
深度学习图像预处理:transforms 核心技术与实战指南

深度学习图像预处理:transforms 核心技术与实战指南

1. 图像预处理到底在解决什么问题 做过深度学习项目的人都有一个共识:模型效果不好,八成不是网络结构的问题,而是数据没喂对。我刚开始接触计算机视觉那会儿,拿到一批图片就直接往网络里塞,结果训练loss震荡得厉害&…

2026/10/13 13:00:12 阅读更多 →
Linux内核模块入门:从Hello World驱动到加载卸载全流程

Linux内核模块入门:从Hello World驱动到加载卸载全流程

1. 项目概述:从“Hello World”开始,真正摸到Linux内核驱动的脉搏你点开这个标题,大概率不是想看教科书式的定义,而是手痒了——想亲手敲几行代码,让自己的程序跑进内核空间,亲眼看着insmod命令执行后&…

2026/10/13 13:00:12 阅读更多 →
Windows下Claude Code安装配置与避坑全攻略

Windows下Claude Code安装配置与避坑全攻略

“Claude Code 在 Windows 上到底能不能用?”——这恐怕是过去一个月我在各种技术群里被问最多的问题。一方面,这个命令行形态的 AI 编程代理确实很香,直接在终端里让它读仓库、改代码、跑测试、提 PR,整个工作流都在一个地方流转…

2026/10/13 12:59:11 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →