Grafana二次开发实战:自定义面板插件与数据源适配
1. 从改个面板到动源码这次二次开发到底要解决什么Grafana 用久了几乎每个团队都会走到同一个岔路口标准面板不够用了。不是缺个图表类型就是缺个交互逻辑再不然就是数据源接入方式跟自家系统对不上。我这次接到的需求说起来也不复杂——要在现有 Grafana 里塞进一套自定义的可视化组件同时让面板支持一种内部特有的数据格式还得保证升级 Grafana 版本时改动不会全废。关键词就三个Grafana 二次开发、自定义面板插件、数据源适配。摘要一句话概括在不 fork 整个 Grafana 主仓库的前提下通过插件机制和少量前端注入实现一套贴合内部业务的可视化扩展。先泼一盆冷水。很多人一听二次开发第一反应是 clone 官方仓库改源码重新编译。这条路我走过也劝你别轻易走。Grafana 的前端代码量非常大Angular 和 React 混用的历史包袱还在构建链路又长你改一个组件可能要等十几分钟才能看到效果。更致命的是一旦你 fork 了主仓库后续官方每次发版你都要手动 merge冲突多到让人怀疑人生。所以这次我定的策略很明确能用插件解决的绝不碰主仓库必须碰主仓库的用最小侵入的方式打补丁。这个原则贯穿了整个开发过程也是后面所有技术选型的判断依据。适合看这篇内容的人大概是这样几类已经会用 Grafana 做基础看板、想进一步做定制的运维或前端同学被标准功能不够用卡住、正在评估二次开发成本的技术负责人以及单纯好奇 Grafana 插件体系是怎么跑起来、想动手试一把的开发者。不需要你是 Grafana 源码专家但至少得跑过一次本地实例知道面板、数据源、插件这几个概念大概指什么。下面我会按真实的开发顺序来讲先搞清楚 Grafana 的扩展点在哪再决定用什么姿势下手然后是环境搭建、插件开发、数据源适配、调试排错最后聊聊升级兼容这个绕不开的坑。每一步我都会说清楚为什么这么做而不是只丢一堆命令给你。2. 先摸清 Grafana 的扩展边界哪些能改哪些别碰动手之前花半天时间把 Grafana 的扩展机制摸清楚能帮你省掉后面好几天的返工。我见过太多人上来就改代码改到一半发现方向错了推倒重来。2.1 三种扩展方式的能力边界对比Grafana 对外提供的扩展能力大致可以分成三层能力从弱到强侵入性也从低到高扩展方式能做什么侵入性升级兼容性适用场景数据源插件接入自定义后端数据无极好内部系统数据接入面板插件自定义可视化组件无极好特殊图表、交互逻辑前端注入/源码补丁改 UI、改交互流程高差插件机制覆盖不到的地方我的建议是优先数据源插件其次面板插件最后才考虑源码补丁。这次的需求里数据格式适配用数据源插件解决可视化组件用面板插件解决只有一处全局的导航栏定制插件机制确实覆盖不到才用了前端注入。2.2 为什么插件机制是二次开发的首选插件机制最大的价值在于它把你的代码和Grafana 的代码彻底隔离开了。你的插件是一个独立的包Grafana 在运行时动态加载它。官方升级 Grafana只要插件的 API 契约没变你的插件基本不用动。这里有个关键概念叫Grafana Plugin SDK它定义了插件和宿主之间的接口。面板插件要实现的核心接口包括PanelPlugin、PanelModel这些数据源插件要实现DataSourceApi那一套。你只要按接口写宿主就能正确调用你。提示插件 API 在不同 Grafana 大版本之间会有变化尤其是从 7.x 到 8.x、9.x 这段前端框架从 Angular 往 React 迁移很多老 API 被标记废弃。选版本时一定要先查官方文档里对应版本的插件开发指南别照着三年前的教程写。2.3 源码补丁的代价我踩过的 merge 地狱说个真实教训。早期有个项目为了改一个面板的默认行为直接改了主仓库里对应的组件文件。当时跑得好好的结果官方发了个小版本那个文件被重构了我们的改动全丢还得重新定位、重新改。更麻烦的是团队里没人记得当初为什么这么改注释也没写清楚排查花了整整两天。从那以后我给自己定了规矩任何对主仓库的改动必须记录在案必须能说清楚为什么插件机制做不到。如果说不清楚那就说明你还没找对扩展点回去继续找。3. 环境搭建把开发链路先跑通再谈功能功能还没开始写环境先折腾了大半天这是常态。Grafana 插件开发的环境有几个容易卡住的点我一个个说。3.1 本地 Grafana 实例的两种跑法跑本地实例有两条路一是直接下载官方二进制包解压运行二是用容器跑。我推荐容器方式原因是干净、可复现、删了不留痕。docker run -d \ --name grafana-dev \ -p 3000:3000 \ -v $(pwd)/plugins:/var/lib/grafana/plugins \ -e GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINSmy-custom-panel \ grafana/grafana:latest这里有两个关键点。第一-v把本地插件目录挂进容器这样你改完插件代码重启容器就能加载新版本不用重新构建镜像。第二GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS这个环境变量必须设否则 Grafana 默认拒绝加载未签名的插件你会看到插件列表里空空如也还以为是代码问题。注意这个环境变量只在开发环境用。生产环境加载未签名插件有安全风险正式发布时应该走签名流程或者至少确保插件来源可信。3.2 插件脚手架的生成与目录结构官方提供了脚手架工具能帮你生成一个符合规范的插件骨架。我建议用它起步别自己从零建目录容易漏掉plugin.json里的必填字段。npx grafana/create-pluginlatest生成出来的目录结构大概是这样my-custom-panel/ ├── src/ │ ├── module.ts # 插件入口导出 PanelPlugin │ ├── plugin.json # 插件元信息 │ └── components/ │ └── SimplePanel.tsx ├── package.json └── tsconfig.jsonplugin.json是插件的身份证里面的id、type、name这些字段决定了 Grafana 怎么识别和展示你的插件。type字段尤其重要写panel就是面板插件写datasource就是数据源插件写错了加载直接失败。3.3 构建与热加载的取舍脚手架默认用的是 webpack 或者 vite 构建。开发阶段我强烈建议开启 watch 模式改完代码自动重新打包配合容器的挂载目录刷新页面就能看到效果。npm run dev但这里有个坑Grafana 对插件的加载是启动时扫描的你重新打包了插件文件Grafana 不一定能感知到。稳妥的做法是改完代码后重启一下 Grafana 容器或者用 Grafana 提供的插件重载接口。我一般图省事直接docker restart grafana-dev几秒钟的事比排查为什么改了没生效划算得多。4. 自定义面板插件从接口契约到渲染落地环境通了开始写真正的功能。面板插件是这次的重头戏我把它拆成几个关键环节来讲。4.1 PanelPlugin 的注册与生命周期面板插件的入口在module.ts核心就是导出一个PanelPlugin实例import { PanelPlugin } from grafana/data; import { SimplePanel } from ./components/SimplePanel; export const plugin new PanelPlugin(SimplePanel) .setPanelOptions((builder) { builder.addTextInput({ path: labelText, name: 标签文字, defaultValue: 默认标签, }); });setPanelOptions这段是给面板配置面板用的。你在 Grafana 右侧的编辑区看到的那些配置项就是这里定义的。path是配置项的键名后面在组件里通过props.options.labelText就能拿到。组件的生命周期很简单Grafana 拿到数据后把data和options作为 props 传给你的组件你负责渲染。数据变了、配置变了组件重新渲染。就这么直接。4.2 数据怎么进到面板里DataFrame 结构解析这是新手最容易懵的地方。Grafana 传给面板的数据不是简单的数组而是一个叫DataFrame的结构。你可以把它理解成一张带元信息的表格interface DataFrame { name?: string; fields: Field[]; // 每一列 length: number; // 行数 }每个Field有name、type、values这些属性。values是一个向量可能是普通数组也可能是 Arrow 格式的向量。取值的时候别直接下标访问用field.values.get(index)更稳妥能兼容不同底层实现。我一开始就是直接field.values[0]在简单数据下没问题换成大数据量、Arrow 向量之后就取不到值了排查了半天才发现是访问方式的问题。这个坑记住能省你不少时间。4.3 渲染层选型为什么我最终用了原生 SVG渲染这块可选方案有 ECharts、D3、原生 SVG/Canvas 几种。我这次的需求是画一个带交互的自定义图形数据量不大但交互逻辑复杂。ECharts 上手快但它的配置式 API 在复杂交互下会变得很别扭你想精细控制某个元素的响应得绕一大圈。D3 灵活但学习曲线陡团队里其他人接手成本高。最后我选了原生 SVG 配合 React理由是交互逻辑完全可控代码可读性好团队里会 React 的人都能改。代价是画图的基本功得自己写比如坐标映射、缩放、拖拽这些。但这次图形不复杂自己写反而更清爽。选型这事没有标准答案关键看你的图形复杂度和团队技术栈别盲目跟风。4.4 面板配置项的设计让使用者不用改代码面板插件好不好用一半看渲染一半看配置项设计。我的原则是凡是使用者可能想调的东西都做成配置项。比如颜色、尺寸、标签文字、是否显示网格线这些全通过setPanelOptions暴露出去。这样业务方换个颜色、调个阈值自己在 Grafana 界面上点几下就行不用来找你改代码重新发版。配置项的类型也很丰富文本、数字、下拉、开关、颜色选择器都有。用对了类型Grafana 会自动给你生成合适的输入控件体验很好。这里有个小技巧给配置项写清楚description鼠标悬停时会显示提示能大幅降低使用者的理解成本。5. 数据源适配把内部数据格式翻译成 Grafana 能懂的话面板画得再好数据进不来也是白搭。这次的数据源适配是另一个核心工作。5.1 DataSourceApi 必须实现的几个方法数据源插件要实现DataSourceApi接口其中最关键的是query方法async query(options: DataQueryRequestMyQuery): PromiseDataQueryResponse { const targets options.targets.filter((t) !t.hide); const data await Promise.all( targets.map((target) this.doQuery(target)) ); return { data }; }options.targets是用户在查询编辑器里配置的查询条件你要遍历它们逐个去后端取数最后返回一个DataFrame数组。Grafana 拿到这些 DataFrame再分发给面板去渲染。除了query还有testDatasource测试连接是否通、getTagKeys、getTagValues用于变量查询这些方法按需实现。testDatasource建议一定要实现用户配置数据源时点保存并测试能立刻知道配置对不对省去大量沟通成本。5.2 内部数据格式到 DataFrame 的映射我们内部系统的数据格式跟 Grafana 期望的不一样需要做一层转换。核心工作是把内部的时间序列数据映射成带time字段和value字段的 DataFrame。import { MutableDataFrame } from grafana/data; const frame new MutableDataFrame({ refId: target.refId, fields: [ { name: time, type: FieldType.time }, { name: value, type: FieldType.number }, ], }); internalData.forEach((point) { frame.appendRow([point.timestamp, point.value]); });这里有个性能注意点别用appendRow逐行追加大数据量几万行下来会明显卡顿。数据量大时直接构造fields的values数组一次性塞进去效率高得多。我实测过同样十万行数据逐行追加要好几秒批量构造基本瞬间完成。5.3 查询编辑器让用户能配出正确的查询数据源插件通常还要提供一个查询编辑器组件让用户配置查询条件。这个组件通过DataSourcePlugin注册export const plugin new DataSourcePlugin(MyDataSource) .setQueryEditor(MyQueryEditor);查询编辑器本质上就是个 React 组件接收query、onChange、onRunQuery这些 props。用户改了什么通过onChange回传Grafana 更新查询状态。设计得好不好直接决定用户配查询顺不顺手。我的经验是能下拉选的别让用户手输能自动补全的别让用户记把复杂度留给自己把简单留给用户。6. 调试与排错那些文档里不会写的坑功能写完了跑起来一堆问题。这一节我按真实排查顺序把遇到的坑一个个还原出来。6.1 插件加载失败从浏览器控制台找线索插件加载失败是最常见的第一个坑。现象是 Grafana 插件列表里看不到你的插件或者看到了但点进去报错。排查第一步打开浏览器开发者工具的控制台。Grafana 加载插件失败时控制台会有明确报错比如plugin.json格式错误、入口文件找不到、依赖缺失等。别急着改代码先看报错说什么。我遇到过一次控制台报Failed to load plugin module查了半天发现是构建产物路径跟plugin.json里声明的module字段对不上。改构建配置或者改plugin.json都行关键是让两边一致。6.2 数据不显示先确认数据到没到面板面板画不出来有两种可能数据没到或者数据到了但渲染逻辑有问题。怎么区分在面板组件里把props.data打印出来。如果props.data是空的问题在数据源或查询配置如果有数据但画面空白问题在渲染。这个二分法能帮你快速定位问题在哪一层别一上来就盯着渲染代码看。我踩过一个坑数据源query方法返回的DataFrame里time字段的类型写成了string而不是FieldType.timeGrafana 认不出这是时间轴面板就画不出来。类型字段一定要写对这是硬性约定。6.3 配置项不生效options 的传递链路检查用户改了配置项面板没反应。这种情况通常是options的传递链路断了。检查顺序是配置项定义里的path是否和组件里读取的键名一致setPanelOptions是否真的注册了组件是否在options变化时重新渲染。React 组件里options是作为 props 传进来的只要父组件更新你的组件就会重新渲染。如果你用了useMemo或者shouldComponentUpdate做了优化记得把options加进依赖否则改了配置不刷新。这个坑很隐蔽因为代码逻辑看起来完全正确。6.4 一个隐蔽的坑时区与时间戳单位这个坑我单独拎出来说因为它太容易被忽略。Grafana 内部时间戳默认是毫秒而我们内部系统的数据是秒。如果不做转换时间轴会显示成 1970 年或者所有点挤在一起。// 秒转毫秒 const timestampMs timestampSec * 1000;另外时区也要注意Grafana 有全局时区设置面板里也可以单独设。数据源返回的时间戳最好是 UTC让 Grafana 去负责展示层的时区转换别在数据源里就转成本地时间否则用户切换时区时会乱套。7. 升级兼容让这次改动活得更久一点二次开发最怕的不是写不出来而是写出来了官方一升级全废。这一节聊聊怎么让改动更抗造。7.1 锁定 API 版本别追新插件开发依赖的grafana/data、grafana/ui这些包版本要跟目标 Grafana 实例对齐。别用最新版也别用太老的版本。我的做法是先确定生产环境 Grafana 的版本然后装对应版本的 SDK 包。{ dependencies: { grafana/data: 9.5.0, grafana/ui: 9.5.0 } }版本对齐之后本地开发和生产环境行为一致能避免本地好好的上线就崩这种糟心事。7.2 把改动清单化为下次升级留后路所有对主仓库的改动我都记在一个单独的文档里写清楚改了哪个文件、改了什么、为什么插件机制做不到、升级时怎么验证。这份文档在下次升级时就是救命稻草。插件部分相对省心但也要记录清楚依赖的 SDK 版本和用到的 API。升级 Grafana 时先查官方 changelog 里有没有废弃你用的 API有的话提前改。7.3 用特性开关隔离风险改动对于不确定会不会出问题的改动我习惯加个特性开关。比如那个导航栏定制我通过配置项控制是否启用出问题了关掉就行不用回滚整个版本。这个习惯在二次开发里特别有用因为你的改动往往没有官方那么充分的测试覆盖。8. 一些实操下来觉得值得说的经验整个项目做下来有几个体会比较深分享给准备动手的人。第一先跑通最小闭环再堆功能。我一开始就想把面板、数据源、配置项全做完结果卡在环境上两天。后来改变策略先让一个最简单的面板能显示一行写死的数据跑通了再逐步加数据源、加配置、加交互。每加一块都能验证出问题也好定位。第二多看官方示例插件的源码。官方仓库里有一批示例插件代码质量高覆盖了各种典型场景。遇到不知道怎么实现的功能去翻示例比看文档快得多。文档告诉你有什么 API示例告诉你 API 怎么组合起来用。第三别怕用 TypeScript。Grafana 的 SDK 类型定义很完整用好类型能帮你在编译期发现大量问题。我一开始嫌类型麻烦用了any结果运行时各种莫名其妙的错误回头补类型反而花了更多时间。第四性能问题要早测。面板渲染和数据查询的性能别等到上线才发现。本地造点大数据量跑一跑看看卡不卡。我那个逐行追加数据的坑就是上线后用户反馈卡顿才发现的早测能早避免。最后说个心态上的事。Grafana 二次开发本质上是在别人的框架里做扩展你得接受有些事就是做不到或者做起来很别扭。遇到这种情况先退一步想想是不是需求本身可以调整而不是硬刚框架。很多时候换个交互方式、换个数据组织方式问题就绕过去了比死磕源码划算得多。

相关新闻

MapWinGis 编译与投影转换实战:从 msvc 工具集到 ocx 注册

MapWinGis 编译与投影转换实战:从 msvc 工具集到 ocx 注册

简介:这份资源包面向使用 Visual Studio 2019(MSVC)开发环境的 GIS 程序员,提供 MapWinGis 开源地理信息系统框架运行与编译所需的全部第三方组件,解决在 Windows 平台搭建 GIS 应用时依赖库缺失、版本不匹配的问题。包…

2026/10/14 2:15:21 阅读更多 →
基于STM32单片机的防火报警系统设计与实现

基于STM32单片机的防火报警系统设计与实现

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

2026/10/14 2:15:21 阅读更多 →
OpenGL实验跑通指南:依赖配置、CMake编译与shader调试实战

OpenGL实验跑通指南:依赖配置、CMake编译与shader调试实战

简介:针对西北工业大学软件学院计算机图形学课程打包的实验代码合集,覆盖VC6.0环境下的多个独立实验项目,适合课程在读学生及希望借助实例入门图形学编程的学习者。压缩包共224个文件,以C源文件(.cpp/.h)、…

2026/10/14 2:15:21 阅读更多 →

最新新闻

Python爬虫实战:手机APP数据抓取与解析全流程

Python爬虫实战:手机APP数据抓取与解析全流程

简介:这份PDF资料面向具备Python基础、对网络请求与爬虫开发感兴趣的技术爱好者与研发工程师,围绕手机APP数据传输的抓取与解析展开,重点讲解如何借助Fiddler抓包定位登录与话题接口,再通过urllib2构造HTTP请求、携带Header与表单…

2026/10/14 3:04:31 阅读更多 →
C++ 对接 S3 对象存储实战:SDK 选型、分片上传与避坑指南

C++ 对接 S3 对象存储实战:SDK 选型、分片上传与避坑指南

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

2026/10/14 3:04:31 阅读更多 →
SQLite安装全指南:嵌入式数据库跨平台配置与常见坑

SQLite安装全指南:嵌入式数据库跨平台配置与常见坑

第一次折腾SQLite的时候,我找了一圈“Setup.exe”,愣是没找到。下载页面全是压缩包,网上一堆教程各写各的,有人说不用装,有人说得编译源码,看得人发懵。后来我才搞明白:SQLite的“安装”逻辑跟M…

2026/10/14 3:04:31 阅读更多 →
C++手写数据结构实战:动态数组、哈希表、AVL树与图算法

C++手写数据结构实战:动态数组、哈希表、AVL树与图算法

简介:《Implementing Useful Algorithms in C》由Dmytro Kedyk撰写,面向具备一定C基础、希望深入掌握算法设计与工程实现的开发者与计算机专业学生。全书从算法正确性证明、渐进记法与机器模型等基本原则切入,系统讲解数据类型选择、内存管理…

2026/10/14 3:04:31 阅读更多 →
本地27B模型配Function Calling:工具不触发的原因与实战

本地27B模型配Function Calling:工具不触发的原因与实战

给本地跑的27B模型配了三个专业工具之后,我做的第一件事就是拿它试聊天。你猜怎么着——工具列表明明挂在接口定义里,模型却像一个看见工具箱、却只肯用手拧瓶盖的人,简单问题全凭语言能力直接答,一个工具都不碰。我一度觉得这套配…

2026/10/14 3:04:31 阅读更多 →
容器内nvidia-smi报Unknown Error的排查与根治

容器内nvidia-smi报Unknown Error的排查与根治

凌晨两点半,工作群里弹出一条消息:某个长期跑的容器,前一秒还好好的,后一秒容器里的nvidia-smi直接报Failed to initialize NVML: Unknown Error。宿主机上执行nvidia-smi一切正常,新建一个临时容器也能正常看到卡&…

2026/10/14 3:03:31 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

2026/10/13 16:22:04 阅读更多 →
影刀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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →