OpenClaw插件开发实战:从零为AI智能体装上执行之手
很多人一开始玩AI智能体都觉得非常惊艳聊着聊着发现它把方案写得头头是道可真让它去读一个本地文件、查一下外部服务、调一下公司内部系统它立刻就懵了。说白了模型再强也只是“大脑”缺少那一双能实际干活的手。我在给一个团队项目做智能化改造时也撞上了同样的墙最后被某个开源智能体框架的插件机制救了——这个框架就是OpenClaw。简单说OpenClaw是一个以插件为核心的AI智能体运行框架把“让智能体做事”这件事拆成了清晰的三步模型决策、插件执行、结果回流。而插件开发就是给智能体装上你想要的那只手。这篇文章适合两类人一类是做智能体应用、想把私有工具或API能力接入模型的开发者另一类是刚开始接触智能体框架、想知道插件机制到底怎么运作的初学者。我会从上层的架构设计一路聊到具体的代码实现、调试技巧和发布规范全部基于我实际跑过一遍的经验不走教科书路子。1. 为什么智能体需要插件制从“会说话”到“会办事”1.1 智能体的能力边界到底在哪很多人把智能体想象成一个全能助手但实际上任何智能体框架的“内置能力”都非常有限。以我常用的OpenClaw为例它内置了会话管理、上下文记忆、prompt编排、多轮推理这些基础能力但仅此而已。它不会读你电脑上的Excel不会操作数据库不会访问外部系统更不会给你的团队IM工具发消息。这不是框架偷懒而是刻意做的边界设计。所有有副作用的行为——调用接口、写文件、改数据——都必须经过明确定义的执行通道。否则智能体就像拿到了万能遥控器模型一个幻觉就能把全公司数据删干净。插件制就是在这个前提下诞生的把每一个“做事”的能力封装成独立单元按需加载、按权限调用、按约定返回。1.2 插件化解决的三个真实痛点第一能力按需加载。你不会想让一个只用来做问答的智能体实例常驻着一堆生产环境的接口凭据。插件系统允许主程序保持精简需要什么能力就挂载什么插件。第二权限边界清晰。每个插件在清单文件里声明自己需要哪些权限比如“读写本地文件”“发起网络请求”。OpenClaw的运行时会在插件加载时校验权限声明并把不满足权限的插件直接挡在门外。这样做就算某个第三方插件有恶意代码也没法越权乱来。第三生态复用。我自己维护的几个内部插件换一套配置就能给项目里的其他智能体用。社区的第三方插件更是五花八门从“汇率查询”到“代码扫描”都有现成的。插件化把开发成果变成了可流通的资产这是单体式智能体项目完全比不了的。2. OpenClaw的插件架构任务分发、工具注册与事件钩子2.1 主循环模型决策和工具执行怎么衔接要写好插件必须先理解OpenClaw主进程的运行循环。大致是四步输入用户消息进入会话框架把消息和上下文一起交给大模型。规划模型根据对话内容和可用工具决定是直接回复还是调用某个工具。执行如果模型决定调用工具框架就会定位到对应的插件执行函数传入模型填好的参数。观察插件执行结果以结构化消息的形式返回给模型模型读到结果后再组织最终回复。插件开发者最需要关心的只有第三个阶段你的代码能拿到什么入参执行完返回什么结构。其他的模型调度逻辑OpenClaw的运行时已经处理好了不需要我们操心。2.2 工具注册表模型是怎么知道你的工具的模型之所以“知道”某个插件的存在靠的是一份工具注册表。OpenClaw的插件管理器在加载插件时会读取每个工具的定义生成一份包含名称、描述、参数Schema的清单并把它注入到模型的调用上下文中。这里有个非常容易被忽视的重点**工具描述的质量直接决定模型会不会正确调用你的工具。**我见过太多人写的工具描述是“处理用户请求”“执行操作”这种抽象到没边的话。大模型看这种描述就像人看一份内容含糊的说明书根本不知道什么时候该用只能靠猜。拿一个待办清单插件举例合格的工具描述长这样将一条任务添加到用户的本地待办清单。当用户说“帮我记一下”“提醒我”“做成待办”时使用此工具。参数content必填是任务的具体内容参数due可选是截止日期。这种描述既说清了功能又给了触发场景还标注了参数要求模型几乎不会用错。写插件代码之前先把描述反复读三遍想象自己是从零开始看这份描述的陌生人能不能准确理解。2.3 生命周期钩子在不同节点插入逻辑除了“工具”这个核心概念OpenClaw插件还支持一系列生命周期钩子让你在智能体运行的不同节点执行自定义逻辑。钩子名称触发时机典型用途on_load插件装载时加载配置、初始化资源on_unload插件卸载时清理资源、断开连接on_session_start每次会话开始时做会话级上下文注入on_tool_call任何工具被调用前做统一鉴权、参数校验on_response模型生成回复后做内容过滤、日志记录我实际用得最多的是on_session_start。很多工具需要知道“现在是谁在问”这个钩子能拿到会话的用户标识我常常往里塞一个用户级配置加载逻辑这样每个工具执行时都能快速获取当前用户偏好。3. 手写第一个插件给智能体加一个“待办清单”工具3.1 初始化工程目录与清单文件开始动手前先建立一个标准插件目录。以claw-todo插件为例claw-todo/ ├── manifest.json ├── main.py └── README.mdmanifest.json是插件的身份证。它至少要包含叫什么名字、版本号、入口文件、需要哪些权限和钩子。我一般会写成这样{ name: claw-todo, version: 1.0.0, entry: main.py, hooks: [on_load], permissions: [user.files.write, user.files.read], dependencies: [] }注意权限字段。这里的user.files.write和user.files.read表示该插件只能读写运行用户目录下被允许的文件无法越界访问系统其他路径。这个权限声明不仅是给框架看的也是给使用者看的——权限越少越容易获得信任。3.2 编写工具执行逻辑main.py里的代码是整个插件的心脏。我会用一个非常轻量的装饰器风格来定义工具OpenClaw的Python SDK提供了tool装饰器能直接把普通函数暴露成可供模型调用的工具from openclaw import tool, plugin_event from openclaw.types import ToolContext import json import os TODO_FILE os.path.join(os.path.expanduser(~), .claw_todo.json) def _load_todos(): if not os.path.exists(TODO_FILE): return [] with open(TODO_FILE, r, encodingutf-8) as f: return json.load(f) def _save_todos(todos): with open(TODO_FILE, w, encodingutf-8) as f: json.dump(todos, f, ensure_asciiFalse, indent2) tool( nametodo_add, description向用户的本地待办清单中添加一条任务。当用户提到要记住、安排、提醒时使用。, params{ content: {type: string, description: 待办任务的具体内容, required: True}, due: {type: string, description: 截止日期格式YYYY-MM-DD可为空, required: False} } ) def add_todo(ctx: ToolContext, content: str, due: str None): todos _load_todos() todos.append({ content: content, due: due, done: False, created_at: ctx.timestamp }) _save_todos(todos) return {ok: True, count: len(todos), message: f已添加待办{content}} tool( nametodo_list, description查看当前待办清单中的全部任务。当用户问有哪些待办、有什么任务要处理时使用。, params{} ) def list_todos(ctx: ToolContext): todos _load_todos() return {ok: True, todos: todos}函数的第一个参数固定是ToolContext里面装着会话ID、用户标识、时间戳等运行信息后面才是模型传入的具体参数。返回时一定要用结构化的字典这样模型的观察阶段才能稳定地解析数据。3.3 本地运行验证写完代码别急着上线先在本地跑一遍。我一般用开发者模式启动OpenClaw加载当前目录的插件claw dev --plugin ./claw-todo然后开一个会话窗口手动输入“帮我记一下明天下午三点开周会”。如果插件工作正常日志里会出现一条工具调用记录类似这样[ToolCall] todo_add - params{content: 明天下午三点开周会, due: null} [ToolResult] {ok: true, count: 1, message: 已添加待办明天下午三点开周会}看到ToolCall和ToolResult成对出现说明工具执行链路已经通了。这时候再多问几句“我现在有什么待办”确认todo_list也能正确返回。第一次跑通的时候会非常有成就感因为这标志着你已经亲手给AI智能体装上了一只能用的手。4. 把插件做成真正可用的工具上下文读取与外部系统对接4.1 读取会话上下文让插件感知“谁在提问”上面那个待办插件有个明显问题所有用户共用同一个清单这在多用户环境里根本没法用。OpenClaw的ToolContext里有user_id只需要在存取文件时按用户隔离就好。TODO_FILE_TEMPLATE os.path.join(os.path.expanduser(~), .claw_todo_{}.json) def _todo_file(user_id: str): safe_id user_id.replace(/, _).replace(\\, _) return TODO_FILE_TEMPLATE.format(safe_id)这个改动看起来简单但它体现了插件开发的一个重要原则把通用能力建立在上下文之上。插件永远不要假设自己只服务一个场景要把上下文数据当水、把用户当容器。这样同一个插件放到不同项目里只要上下文里提供了正确的用户标识照样能工作。4.2 对接外部系统的常见方式待办清单只是本地读写真实项目里插件最常干的活是对接外部HTTP服务。我以“天气查询”为例演示一个调用HTTP API的插件工具。import requests tool( nameweather_query, description查询某个城市当前的实时天气。当用户询问天气、温度、降雨情况时使用。, params{ city: {type: string, description: 城市名称如 北京、上海, required: True} } ) def query_weather(ctx: ToolContext, city: str): url fhttps://weather.example.com/api/v1/current?city{city} resp requests.get(url, timeout10) if resp.status_code ! 200: return {ok: False, error: f天气服务返回状态码 {resp.status_code}} data resp.json() return { ok: True, city: data[city], temperature: data[temp_c], condition: data[sky_text] }这里有两个点需要特别注意。第一是超时不能省。插件一旦阻塞整个智能体的响应流程都会被拖着几秒十几秒没响应用户早就没了耐心。第二是凭证不进代码。如果接口需要密钥不要直接写在源码里而是通过OpenClaw的环境变量注入机制读取配置。我通常的做法是在manifest.json里声明secrets字段运行时从密钥环加载插件代码里通过ctx.get_secret(WEATHER_API_KEY)取用既安全又方便换配置。4.3 多工具之间怎么传数据新手常常问一个问题“插件A能不能直接调用插件B的工具”OpenClaw的设计原则是不鼓励插件间直接调用因为这会打破权限隔离和日志链路。插件间传数据正确姿势是把数据放到会话上下文的暂存区让模型来决定下一步该用哪个工具。举个实际例子一个“速报生成”场景需要先查天气再生成文案。你会想写一个工具去调另一个工具但实际上更好的做法是注册两个独立工具让模型自己在一次会话里依次调用。模型先调weather_query拿到天气数据再把数据作为输入参数去调report_generate。这样两步调用留在日志里出了问题非常容易排查而且每个插件的权限边界依旧清晰。5. 调试与错误恢复我在实测里踩过的四个坑5.1 工具注册成功但智能体就是不调用这个问题我遇到不下十次。现象是插件加载无报错工具列表里也有名字可模型就是无视它自顾自地编一段文字回复。排查链路是这样的第一步打开debug日志确认工具确实进了注册表claw dev --plugin ./weather --debug日志里会列出所有已注册工具如果看不到weather_query说明是注册阶段的问题检查tool装饰器是否被正确加载。第二步如果工具在注册表里问题就出在描述上。我复盘过几次每次模型不调用的工具描述都是那种特别空洞的比如“执行天气查询”“处理用户请求”。把描述改成我在第2章写过的那种带触发场景的写法模型立刻就开始正确调用了。这一步不需要改代码逻辑但效果立竿见影。5.2 参数Schema字段名与函数入参不一致还有一次我在工具定义里写了参数名keyword但函数签名用的是query。结果模型按照Schema填了keyword框架把调用路由到函数时发现没有这个位置参数直接抛了异常。查了半小时才醒悟过来Schema里写的参数名必须和函数签名里的参数名完全一致。这个坑特别隐蔽因为Python的**kwargs能吞掉很多错误有些错误直到真正调用时才炸出来。为了避免这种问题我后来在本地测试时都会用框架自带的模拟调用功能直接在命令行里指定参数名调用工具而不是每次都靠模型去触发。claw call weather_query --param city杭州这一步能覆盖大多数参数名和类型错误强烈建议写进每个插件的自测流程。5.3 同步HTTP请求把整个智能体卡死早期我在插件里写过一段没设超时的requests.get正好外部服务挂起结果整整三分钟智能体没有任何响应用户疯狂发消息催。后来我统一做了三件事所有网络请求都设置显式超时通常是timeout10增加重试机制使用指数退避第一次等2秒、第二次4秒、第三次8秒最多三次每次重试之间记录日志方便事后分析外部服务的稳定性。def request_with_retry(url, max_try3): for i in range(max_try): try: return requests.get(url, timeout10) except requests.RequestException as exc: if i max_try - 1: raise exc time.sleep(2 ** i)这套处理写完以后至少再没出现过插件把整个智能体拖死的情况。5.4 远程插件断连的问题OpenClaw支持把插件跑在远端进程再用本地方便调试。这个模式省资源、隔离好但也带来一个麻烦远端进程偶尔会因为网络波动断连插件里的状态全部丢失。我最后是靠三个手段解决的加一个心跳探活机制每隔30秒检查一次远端进程状态给工具调用加断线重试每次执行完关键操作主动同步状态回本地磁盘。这一套组合下来断连率从每天好几次降到一周一两次而且断连后插件会自动恢复不需要人工介入。6. 发布、版本兼容与回归测试插件上线前的最后一公里6.1 打包发布前的清单完整性检查插件写好了、调试通过了接下来要给其他人用。OpenClaw的插件发布方式是把目录打包成.ocpkg文件发布到插件仓库或直接分发。打包之前我固定会检查四件事manifest.json里的version跟代码里标注的版本一致所有第三方依赖写进了dependencies且版本范围宽松到兼容主流版本README里有清晰的“功能说明、权限说明、参数说明”三段内容本地已经跑过两次完整工具调用确认无半截报错。如果检查通过执行一个命令就可以出包claw plugin pack ./claw-todo6.2 版本兼容策略插件一旦发出去用户升级框架或升级插件时最容易出问题。我采用了一个比较保守的版本策略major版本号表示不兼容的架构变化minor版本号表示新增功能但向后兼容patch表示修复问题。在manifest.json里用依赖约束来控制这个节奏{ compatibility: { openclaw: 0.8.0,1.0.0 } }这样用户装插件时OpenClaw运行时能自动判断这个插件适不适合当前框架版本避免因为接口签名变化导致插件加载失败。我自己吃过一次亏有个内部插件从旧版本接口迁移到新版本时忘了更新compatibility区间结果框架升级后插件全线不可用排查了很久才发现是版本限制没跟上。6.3 最小回归测试清单每次改完插件我建议至少跑一遍最小回归测试覆盖以下四类调用测试场景用例预期结果正常输入“加一条待办周五交付周报”返回成功清单数1缺必填参数“加一条待办”不给content框架层拦截返回参数校验错误边界输入超长文本、空字符串插件不崩溃要么正常返回要么明确报错异常恢复外部服务返回500插件返回错误信息智能体如实告知用户这个清单虽然简单但覆盖了大多数使用中会碰到的失败模式。我以前写完代码总是直接发出去结果用户各种姿势出问题后来老老实实跑这把测试线上反馈立刻安静了很多。我在实际维护插件项目的过程中最大的体会是插件开发本身不难难的是想清楚“边界”和“接口”。好的插件往往是只做一件事、把接口定义到让你不用看源码也敢用。每次写新插件之前我都会先写下工具描述把它当作写给另一个开发者的求职简历来打磨想不清楚就动手写代码大概率会返工。如果你正准备给智能体加新能力我建议从最小、最具体的工具入手先跑通一次完整的“模型决策到插件执行”链路再逐步扩展。这条路上踩过的每一个坑都会变成你下一个插件写得比别人快的原因。

相关新闻

智能家居MVP实战项目全流程解析:从市场调研到数据埋点

智能家居MVP实战项目全流程解析:从市场调研到数据埋点

简介:一份适合产品经理、产品新人及转岗互联网从业者的实战案例文档,覆盖互联网平台和智能硬件两大产品类型,完整呈现从需求调研、竞品分析、产品定位、路线图规划,到UI/UX交互设计、开发测试、发布推广与项目复盘的全链路方法。文…

2026/10/17 14:46:29 阅读更多 →
SSL证书有效期缩至200天,多子域名自动化续期实践指南

SSL证书有效期缩至200天,多子域名自动化续期实践指南

最近圈子里聊得最多的话题,就是SSL证书有效期要从过去的一年左右进一步压缩到200天。如果你手头管着一堆子域名网站,还停留在手动买证书、手动部署、手动续期的老流程上,那接下来这一轮调整可能会让你非常难受。这不是小打小闹的政策微调&…

2026/10/17 14:46:29 阅读更多 →
SysML语义机制深度解析:Profile重载与系统工程建模本质

SysML语义机制深度解析:Profile重载与系统工程建模本质

简介:本资源是系统工程领域权威入门读物《Systems Engineering with SysML/UML》的完整PDF电子版,面向系统工程师、嵌入式开发人员、MBSE(基于模型的系统工程)实践者及高校相关专业师生,旨在解决复杂系统建模中需求追踪…

2026/10/17 14:45:28 阅读更多 →

最新新闻

Kepware OPC DA Client配置实战:三层结构与QC质量码诊断

Kepware OPC DA Client配置实战:三层结构与QC质量码诊断

简介:本资源是一份面向工业物联网(IIoT)开发者与自动化工程师的Kepware OPC DA Client实战入门教程,聚焦于使用KEPServerEX构建稳定、高效的数据采集通道。内容从零开始详解项目(Project)创建、Channel配置…

2026/10/17 21:00:06 阅读更多 →
蓝桥杯省赛“直线”试题解析:整点去重与精确计数

蓝桥杯省赛“直线”试题解析:整点去重与精确计数

第十二届蓝桥杯省赛的试题C“直线”,是我印象里非常典型的“看着简单,做起来全是坑”的结果填空题。题目只有一句话:平面上有2021个整点,横坐标范围是0到19,纵坐标范围是0到20,问这些点一共能确定多少条不同…

2026/10/17 21:00:06 阅读更多 →
RTSP日志实战:从协议交互到问题定位的完整指南

RTSP日志实战:从协议交互到问题定位的完整指南

1. 从"rtsp 日志"这四个字里能挖出什么搞流媒体的人看到"rtsp 日志"这个标题,第一反应多半是又爱又恨。爱的是,RTSP协议本身并不复杂,抓包加日志基本能把问题定位到八九不离十;恨的是,真正到了生产…

2026/10/17 21:00:06 阅读更多 →
全网最全的AI软件测试面试题(含答案+文档):用TaoToken统一Key跑通面试题里的API测试场景

全网最全的AI软件测试面试题(含答案+文档):用TaoToken统一Key跑通面试题里的API测试场景

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

2026/10/17 21:00:06 阅读更多 →
在VS Code中用MCP集成AI修图:NanoBanana实战指南

在VS Code中用MCP集成AI修图:NanoBanana实战指南

1. 先说结论:为什么要把修图能力塞进代码编辑器1.1 写代码的人其实每天都在“修图”你可能觉得修图是设计师的事,但只要你写过项目文档、搭过个人网站、做过自动化脚本、维护过电商后台,就一定会遇到这种场景:产品图带水印、截图背…

2026/10/17 21:00:06 阅读更多 →
GEE极端降水指数计算全流程:从CHIRPS数据到R95p趋势分析

GEE极端降水指数计算全流程:从CHIRPS数据到R95p趋势分析

简介:这份资源是一套基于Google Earth Engine(GEE)与NASA GPM IMERG V07数据的极端降水分析代码包,服务于遥感、水文与灾害风险研究场景,也适合有一定GEE基础、希望直接复用降水分析脚本的开发者。代码完整覆盖从研究区…

2026/10/17 20:59:06 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04: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/16 21:54:38 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →