DeepSeek API 环境搭建与调用避坑指南:从 401 报错到流式输出

简介:这份资源是一份面向具备一定编程经验的开发者与研究人员的 DeepSeek API 调用指南,围绕从注册账号、获取 API Key 到完成流式输出的完整链路展开,帮助读者快速将 DeepSeek 的语言理解与生成能力集成进聊天机器人、客服系统、智能助理等文本交互类项目。压缩包内共 1 个 docx 文档,体积约 13KB,以图文与代码片段结合的方式组织内容,便于边看边改。文档覆盖环境准备与 OpenAI SDK 安装、基于 OpenAI SDK 与原生 HTTP 请求两种调用示例、请求与响应数据的解析及 429、503 等错误处理,并进一步讲解 temperature、top_p、max_tokens 等生成参数调节,以及 stream=True 流式输出和多轮对话上下文维护的实现方式。文末还提示 API Key 保护、速率限制监控与使用条款遵守,并附官方文档入口。目前已有 3160 人学习,适合希望快速上手并深入理解 DeepSeek 接口集成的读者参考。

1. 从一次 401 报错说起:DeepSeek API 环境搭建到底卡在哪

上周帮一个做智能客服的团队排查问题,他们的 Python 脚本调 DeepSeek API 一直返回 401,换了三台机器、重装了两次 openai 库都没用。最后发现是.env文件里 API Key 多复制了一个换行符——这种玄学问题,在 API 接入阶段太常见了。DeepSeek API 调用本身不复杂,但环境搭建这一步踩坑的人特别多:Python 版本不对、openai SDK 版本和代码写法不匹配、API Key 权限没开、流式输出卡住不返回。这篇笔记按我实际部署的顺序,从注册账号、装依赖、写客户端、配参数到流式输出验证,一步步走完。适合刚拿到 API Key 准备接入的开发者,也适合从 OpenAI 迁移过来、想确认 DeepSeek 兼容性边界的老手。

2. 环境准备与依赖安装:Python 版本和 openai SDK 怎么选

2.1 Python 3.8+ 的版本取舍

项目正文写的是 Python 3.8 或更高版本,这个下限没问题,但实际部署时我一般会拉到 3.10 或 3.11。原因有两个:一是 3.8 已经进入维护末期,部分新版的httpx、pydantic依赖在 3.8 上装最新版会报兼容错误;二是流式输出场景下,3.10+ 对async for和生成器的处理更稳定,不容易出现 chunk 丢失。

检查当前版本:

python3 --version # 输出示例:Python 3.10.12

如果版本低于 3.8,先升级。Ubuntu/Debian 系可以用 deadsnakes PPA,macOS 用 Homebrew,Windows 直接去 python.org 下安装包。我一般不建议用系统自带的 Python 直接装依赖,容易和系统包管理器打架,用venv隔离最省心:

# 创建虚拟环境 python3 -m venv deepseek-env # 激活(Linux/macOS) source deepseek-env/bin/activate # 激活(Windows PowerShell) # .\deepseek-env\Scripts\Activate.ps1

激活后命令行前面会出现(deepseek-env)标识,后续所有 pip 安装都只影响这个环境,装坏了直接删目录重来,不用动系统 Python。

2.2 安装 openai SDK 与版本确认

DeepSeek API 兼容 OpenAI 的接口格式,所以直接用 OpenAI 官方 SDK 就行:

pip install openai # 确认安装版本 pip show openai # 输出示例:Version: 1.30.1

这里有个版本分水岭要注意:openai SDK 在 1.0.0 版本前后 API 写法完全不同。项目正文里的示例代码用的是openai.ChatCompletion.create()这种旧版写法,如果你装的是 1.x 版本,这段代码会直接报AttributeError: module 'openai' has no attribute 'ChatCompletion'。我一般会装 1.x 版本,然后用新版客户端写法,后面章节的代码都按 1.x 来。

如果你确实需要用旧版写法,可以锁定版本:

pip install "openai<1.0.0"

但我不推荐,旧版 SDK 已经停止维护,而且和新版 Python 的兼容性越来越差。直接上 1.x,代码改几行的事。

2.3 用 requirements.txt 固化环境

部署到服务器时,别手动一个个 pip install,写个requirements.txt:

openai>=1.30.0 requests>=2.31.0 python-dotenv>=1.0.0

python-dotenv是用来管理 API Key 的,避免把密钥硬编码在代码里。安装:

pip install -r requirements.txt

这样换机器、重建环境时一条命令搞定,版本也可追溯。我吃过亏——有次在本地跑得好好的,部署到服务器上因为 openai 版本差了 0.2,流式输出的解析逻辑直接崩了,排查了半天。从那以后所有 API 项目都强制走 requirements.txt。

3. API Key 获取与客户端初始化:两种调用方式怎么选

3.1 注册账号与创建 API Key

访问 DeepSeek 官网,点击「API 开放平台」入口创建账号。登录后在「API Keys」页面创建密钥。这里有几个操作细节:

  • 创建时给 Key 起个有意义的名字,比如chatbot-prod、test-local,后面排查用量时能对上号
  • Key 只在创建时完整显示一次,关掉页面就看不到了,必须当场复制保存
  • 不要重复创建,每个账号的 Key 数量有限,而且多把 Key 混用容易搞不清哪把对应哪个服务

保存到本地时,我一般用.env文件:

# .env 文件内容 DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx

然后在.gitignore里加上.env,防止误提交到代码仓库。这个操作看起来基础,但我见过至少三个团队把 Key 提交到了公开仓库,最后只能紧急吊销重发。

3.2 使用 OpenAI SDK 初始化客户端

装好 1.x 版 SDK 后,客户端初始化写法如下:

import os from openai import OpenAI from dotenv import load_dotenv # 加载 .env 文件中的环境变量 load_dotenv() # 初始化客户端,指定 DeepSeek 的 base_url client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com/v1" ) # 发起对话请求 response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "user", "content": "你好,请介绍 DeepSeek 的功能。"} ] ) # 提取回复内容 print(response.choices[0].message.content)

关键参数说明:base_url必须指向https://api.deepseek.com/v1,不写的话默认走 OpenAI 的地址,会直接报连接错误。model指定deepseek-chat,这是对话模型。messages是标准的消息数组,role可以是user、assistant、system。

3.3 使用 HTTP 请求直接调用

如果你不想引入 openai SDK,或者项目本身是轻量级的,直接用requests发 HTTP 请求也行:

import os import requests from dotenv import load_dotenv load_dotenv() url = "https://api.deepseek.com/v1/chat/completions" headers = { "Authorization": f"Bearer {os.getenv('DEEPSEEK_API_KEY')}", "Content-Type": "application/json" } data = { "model": "deepseek-chat", "messages": [ {"role": "user", "content": "你好,请介绍 DeepSeek 的功能。"} ] } response = requests.post(url, headers=headers, json=data) # 检查 HTTP 状态码 if response.status_code == 200: result = response.json() print(result["choices"][0]["message"]["content"]) else: print(f"请求失败,状态码:{response.status_code}") print(response.text)

两种方式怎么选:SDK 方式代码更简洁,自带重试和类型提示,适合正式项目;HTTP 方式依赖少,适合快速验证或嵌入到已有请求框架里。我一般先用 HTTP 方式确认 Key 和网络没问题,再切到 SDK 写业务逻辑。

4. 请求参数配置与响应处理:temperature 和错误码怎么调

4.1 生成参数的实际取值建议

DeepSeek API 支持多个生成参数,调参直接影响输出质量。我按实际使用经验给一组参考值:

参数作用推荐范围使用场景
temperature控制随机性0.3~0.7客服问答用 0.3,创意写作用 0.7
top_p核采样阈值0.8~0.95一般保持默认,和 temperature 二选一调
max_tokens最大生成长度512~4096按业务需求设,设太大浪费额度
frequency_penalty降低重复词0~0.5长文本生成时设 0.3 左右
presence_penalty鼓励新话题0~0.5对话场景设 0.2 避免绕圈子
stop停止序列按需比如设["\n\n"]控制段落数

配置示例:

response = client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": "你是一个技术客服助手,回答简洁准确。"}, {"role": "user", "content": "Python 怎么读取 JSON 文件?"} ], temperature=0.3, # 客服场景降低随机性 max_tokens=1024, # 控制回复长度 frequency_penalty=0.3 # 减少重复表述 )

system消息用来设定角色和行为边界,放在messages数组第一位。这个在客服、助手类场景里很关键,不设的话模型回复风格会飘。

4.2 错误码处理与重试逻辑

API 调用最常见的错误码是 429(速率限制)和 503(服务暂时不可用)。生产环境必须加重试:

import time from openai import OpenAI, APIError, RateLimitError client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com/v1" ) def call_with_retry(messages, max_retries=3): for attempt in range(max_retries): try: response = client.chat.completions.create( model="deepseek-chat", messages=messages ) return response.choices[0].message.content except RateLimitError: # 429 错误,指数退避重试 wait = 2 ** attempt print(f"触发速率限制,{wait} 秒后重试...") time.sleep(wait) except APIError as e: # 其他 API 错误,记录并抛出 print(f"API 错误:{e.status_code} - {e.message}") raise raise Exception("重试次数耗尽,请求失败")

指数退避的意思是第一次等 1 秒,第二次等 2 秒,第三次等 4 秒。不要用固定间隔重试,容易在服务端压力大时持续撞墙。另外,429 不一定是你的问题,也可能是账号级别的并发限制,遇到频繁 429 先去控制台看用量面板。

4.3 多轮对话的上下文维护

多轮对话的核心是把历史消息拼回messages数组:

# 维护一个对话历史列表 conversation_history = [ {"role": "system", "content": "你是一个技术助手。"} ] def chat(user_input): # 追加用户消息 conversation_history.append({"role": "user", "content": user_input}) response = client.chat.completions.create( model="deepseek-chat", messages=conversation_history ) reply = response.choices[0].message.content # 追加助手回复,保持上下文完整 conversation_history.append({"role": "assistant", "content": reply}) return reply # 连续对话 print(chat("什么是 REST API?")) print(chat("它和 GraphQL 有什么区别?")) # 模型能理解"它"指 REST API

注意上下文长度会随着对话轮次增长,token 消耗也线性增加。长对话场景要加截断策略,比如只保留最近 10 轮,或者对历史消息做摘要压缩。我一般会在conversation_history超过一定长度时,把最早的几条user/assistant消息删掉,保留system消息不动。

5. 流式输出配置与避坑排查

5.1 stream=True 的正确写法

流式输出是对话类产品的标配,用户不用等完整回复生成完就能看到内容逐字出现。开启方式:

response = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "写一段关于 Python 装饰器的介绍。"}], stream=True # 开启流式输出 ) # 逐块读取并打印 for chunk in response: if chunk.choices and chunk.choices[0].delta.content: content = chunk.choices[0].delta.content print(content, end="", flush=True)

关键点:chunk.choices[0].delta.content可能为None,尤其是第一个 chunk 和最后一个 chunk,必须判空。flush=True保证内容实时输出,不加的话在某些终端里会攒一批才显示。流式模式下最后一个 chunk 的finish_reason会是stop,可以用这个判断生成结束。

5.2 避坑排查:五个高频翻车现场

现象一:401 Unauthorized。原因通常是 API Key 错误或格式不对。解决:检查.env文件里 Key 有没有多余空格或换行,确认Authorization头是Bearer sk-xxx格式,注意Bearer和 Key 之间有一个空格。

现象二:AttributeError: module 'openai' has no attribute 'ChatCompletion'。原因是装了 1.x 版 SDK 但用了 0.x 的写法。解决:要么降级 SDK 到 0.28,要么把代码改成client.chat.completions.create()的新版写法。我建议改代码,别降级。

现象三:流式输出卡住不返回。原因可能是网络中间层缓冲了响应,或者没设flush=True。解决:先确认终端支持实时输出,再检查是否有反向代理层做了缓冲。如果是 Web 服务,确保框架的流式响应配置正确。

现象四:429 Too Many Requests 频繁出现。原因是请求频率超过账号限制。解决:加指数退避重试,降低并发数,或者去控制台确认当前套餐的速率上限。不要用多把 Key 轮换来绕过限制,容易被风控。

现象五:响应内容截断。原因是max_tokens设太小。解决:调大max_tokens,但注意模型有最大输出长度限制,超长内容需要分段请求。另外检查finish_reason是不是length,是的话说明被截断了。

5.3 生产环境的 Key 管理习惯

API Key 泄露的后果不用多说,我一般坚持几条规矩:Key 只存在环境变量或密钥管理服务里,绝不写进代码;.env文件必须进.gitignore;不同环境(开发、测试、生产)用不同的 Key,方便单独吊销;定期在控制台检查用量,发现异常调用立刻换 Key。这些操作花不了几分钟,但能避免大麻烦。

6. 装完怎么验证:从单次请求到流式输出的完整检查清单

环境搭完之后,别急着写业务代码,先跑一遍验证流程,确认每一层都通了。我一般按这个顺序检查:

第一步,确认 Python 和依赖版本。在虚拟环境里执行:

python3 --version && pip show openai | grep Version

输出应该是 Python 3.8+ 和 openai 1.x。如果 openai 显示 0.x,说明装错了版本,回到 2.2 节重装。

第二步,验证 API Key 有效性。用最简单的 HTTP 请求测:

import os, requests from dotenv import load_dotenv load_dotenv() resp = requests.post( "https://api.deepseek.com/v1/chat/completions", headers={"Authorization": f"Bearer {os.getenv('DEEPSEEK_API_KEY')}"}, json={"model": "deepseek-chat", "messages": [{"role": "user", "content": "ping"}]} ) print(resp.status_code) # 期望 200

返回 200 说明 Key 和网络都正常。返回 401 检查 Key,返回 403 检查账号权限,返回超时检查网络连通性。

第三步,验证 SDK 调用链路。跑一遍 3.2 节的 SDK 示例代码,确认能正常拿到回复。这一步过了,说明 SDK 版本、base_url、参数格式都没问题。

第四步,验证流式输出。跑 5.1 节的流式代码,观察内容是否逐字输出。如果一次性全部打印出来,说明stream=True没生效或者被缓冲了。

第五步,验证多轮对话。连续发两轮消息,确认模型能理解上下文指代。如果第二轮回复答非所问,检查conversation_history有没有正确追加assistant消息。

第六步,验证错误处理。故意用一个错误的 Key 跑一次,确认重试逻辑和错误提示符合预期。这一步很多人跳过,但生产环境出问题时,好的错误处理能省大量排查时间。

我习惯把这六步写成一个verify.py脚本,每次换机器或重建环境后跑一遍,全绿了再开始写业务代码。有一次部署到新服务器,前五步都过了,第六步发现 429 重试逻辑里time.sleep被写成了time.sleep(0),等于没退避,上线后直接被限流打挂。从那以后我每次配完环境都强制走一遍完整验证,不跳步。希望帮到你。

本文还有配套的精品资源,点击获取

相关新闻

大语言模型合成数据与记忆机制:从生成范式到RAG的工程实践

大语言模型合成数据与记忆机制:从生成范式到RAG的工程实践

1. 从"数据不够用"说起&#xff1a;为什么合成数据突然成了刚需做模型训练的人都有一个共同的痛&#xff1a;真实数据永远不够。不是数量不够&#xff0c;是质量够、分布对、标注准的数据不够。我接触过不少做垂直领域模型微调的团队&#xff0c;十个里面有八个卡在同…

2026/10/20 20:17:44 阅读更多 →
AgentScope实战解析:多智能体协同与消息驱动架构的核心机制

AgentScope实战解析:多智能体协同与消息驱动架构的核心机制

1. 项目概述&#xff1a;大模型时代的智能体协同难题&#xff0c;AgentScope凭什么接得住先说结论&#xff1a;AgentScope是我这两年用过的多智能体开发框架里&#xff0c;工程化完成度最高、对大模型原生适配最彻底的一个。它的出现直接解决了我之前搭建多智能体应用时最头疼的…

2026/10/20 20:17:44 阅读更多 →
DeepSeek开源Agent框架插件化架构深度拆解

DeepSeek开源Agent框架插件化架构深度拆解

1. 打开源码那一刻&#xff1a;插件化比我想象的要深得多把这套名为 DeepSeek 的开源 Agent 框架下载到本地时&#xff0c;我原本只是想试试它自带的工具调用能力。Agent 项目这几年看得不少&#xff0c;架构套路基本就那么几类&#xff1a;模型调用 提示词模板 工具列表 循…

2026/10/20 20:17:44 阅读更多 →

最新新闻

TensorFlow鞋类识别实战:Fashion MNIST图像分类全流程

TensorFlow鞋类识别实战:Fashion MNIST图像分类全流程

前段时间重新整理之前的深度学习实验笔记&#xff0c;翻到鞋类识别&#xff08;Fashion MNIST&#xff09;这个项目时&#xff0c;觉得特别值得拿出来单独写一篇。原因很简单&#xff1a;很多人入门深度学习时&#xff0c;都会被建议先跑通手写数字识别&#xff08;MNIST&#…

2026/10/20 20:57:37 阅读更多 →
用AI辅助Kamailio路由排错:一场人机讨论实录与故障切换实践

用AI辅助Kamailio路由排错:一场人机讨论实录与故障切换实践

最近接手一套老的SIP通信平台&#xff0c;核心信令是Kamailio撑着。业务要新增一个较复杂的路由策略&#xff1a;同一个主叫号码&#xff0c;在不同时间段、针对不同被叫前缀&#xff0c;要分别走不同的网关&#xff1b;而其中一路网关做故障切换时&#xff0c;还要保证已经建立…

2026/10/20 20:57:37 阅读更多 →
LibVLC轻量级视频播放模块:嵌入C/C++工程的硬件解码方案

LibVLC轻量级视频播放模块:嵌入C/C++工程的硬件解码方案

简介&#xff1a;本资源是一份基于C/C与VLC开源库开发Windows视频播放器的完整工程实践包&#xff0c;面向具备基础C编程能力、希望掌握多媒体开发技能的中级开发者及高校课程设计学习者。资源聚焦于在Visual Studio 2022环境下集成libvlc实现视频加载、播放控制、窗口渲染等核…

2026/10/20 20:57:37 阅读更多 →
AI Agent创业团队如何配置一支高效的Harness Engineering团队

AI Agent创业团队如何配置一支高效的Harness Engineering团队

1. 为什么需要一支专门的Harness Engineering团队先从一个很现实的场景说起&#xff1a;你所在的创业团队花三个月做出了一个基于大模型的Agent产品Demo&#xff0c;演示给投资人看时效果炸裂&#xff0c;但真正进入客户试用阶段&#xff0c;问题接踵而来——模型给出正确意图却…

2026/10/20 20:57:37 阅读更多 →
视觉语言模型与RGB-D感知融合:三条进化主线与工程实践

视觉语言模型与RGB-D感知融合:三条进化主线与工程实践

1. 从 Transformer 到多模态感知的演进逻辑视觉语言模型和 RGB-D 感知这两个方向&#xff0c;过去几年基本是各走各的路。做 VLM 的人盯着图文对齐、指令跟随、视觉问答这些任务&#xff0c;做 RGB-D 的人则埋头于点云配准、深度补全、SLAM 建图。但最近一年&#xff0c;这两条…

2026/10/20 20:57:37 阅读更多 →
VTI介质地震波数值模拟:MATLAB实现与各向异性正演避坑指南

VTI介质地震波数值模拟:MATLAB实现与各向异性正演避坑指南

简介&#xff1a;这份资源面向地震勘探与地球物理方向的学习者和研究者&#xff0c;提供一套基于Matlab的VTI介质地震波场数值模拟程序&#xff0c;用于分析各向异性介质中地震波的传播规律。VTI介质在垂直与平行层理方向上纵波速度存在差异&#xff0c;是实际地壳中常见的各向…

2026/10/20 20:56:32 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →