多模态Agent如何自主规划UI测试路径:视觉与DOM融合实战

1. 多模态 Agent 做 UI 测试,到底难在哪

UI 测试这件事,做过的人都懂——写起来不难,维护起来要命。一个按钮挪了位置、一个弹窗改了文案、一个加载动画多转了半秒,传统基于选择器的脚本就可能集体罢工。而多模态 Agent 的介入,本质上是想解决一个核心矛盾:让机器像人一样"看着"界面做判断,而不是死记硬背 DOM 结构。

所谓多模态,在这里指的是 Agent 能同时处理多种输入信号——截图(视觉)、DOM 树(结构)、可访问性树(语义)、甚至操作日志(时序)。规划 UI 测试路径,就是让 Agent 根据当前界面状态,自主决定"下一步点哪里、输入什么、验证什么",而不是沿着一条写死的脚本往下跑。

这套东西适合谁?如果你正在做自动化测试、质量保障、或者对 AI Agent 落地感兴趣,这篇文章会从架构设计一路讲到实操踩坑。我默认你有基本的测试概念,但不需要你懂多模态模型训练——我们聊的是工程落地,不是发论文。

核心关键词先摆出来:多模态 Agent、UI 测试路径规划、视觉定位、DOM 融合、自主探索、路径剪枝。后面所有内容都围绕这几个词展开。

为什么突然聊这个?因为过去一年我参与了一个模拟项目,目标是让 Agent 自主完成一套跨平台系统的回归测试。过程中踩的坑、试过的方案、最后跑通的路径规划逻辑,我觉得值得完整复盘一遍。下面按"设计思路→核心细节→实操过程→问题排查"四块来讲,每块都会给到可直接参考的参数和代码片段。

2. 整体架构设计与路径规划思路拆解

2.1 为什么不能只用视觉,也不能只用 DOM

先说一个我踩过的坑。最开始我们想得很简单:既然是多模态,那就截图丢给视觉模型,让它输出点击坐标不就完了?实测下来,纯视觉方案在三种场景下必挂:

  • 文本输入框:视觉模型能定位到框,但不知道当前框里已有什么内容,容易重复输入或清空失败。
  • 动态列表:滚动加载的列表项,视觉上长得一模一样,模型分不清"第3项"和"第7项"。
  • 隐藏状态:按钮的 disabled 状态在截图上可能只是颜色浅了一点,模型经常误判为可点击。

反过来,纯 DOM 方案也有硬伤:Canvas 绘制的图表、WebGL 渲染的 3D 场景、跨 iframe 的嵌套结构,DOM 树里根本找不到有效信息。所以结论很明确——视觉负责"看到什么",DOM 负责"是什么",两者必须融合。

我们的融合策略是这样的:以 DOM 节点为锚点,把每个可交互元素的边界框(bounding box)投影到截图上,形成"视觉-结构"对齐的候选操作集。Agent 的每一步决策,都是在这个候选集里做选择,而不是在整张图上自由发挥。这样做的好处是,路径规划的空间从"无限像素"缩小到"有限元素",搜索效率提升了一个数量级。

2.2 路径规划的三层决策模型

Agent 规划测试路径,我把它拆成三层:

第一层是目标层:当前测试用例要验证什么?比如"验证登录失败三次后账号锁定"。这一层由测试用例描述驱动,Agent 需要把自然语言目标拆解成可执行的验证点。

第二层是策略层:为了达成目标,需要经过哪些中间状态?比如"打开登录页→输入错误密码→点击登录→重复三次→检查提示文案"。这一层是路径规划的核心,Agent 需要根据当前界面状态动态调整。

第三层是动作层:具体每一步做什么?点击哪个元素、输入什么文本、等待多久。这一层直接对接执行引擎。

三层之间的关系是:目标层定方向,策略层定路线,动作层定脚步。很多团队做 Agent 测试失败,就是因为把三层混在一起,让模型一次性输出"点这里输入那个",结果稍微有点偏差就全盘崩溃。

提示:策略层一定要保留"回退"能力。当某个动作执行后界面状态不符合预期,Agent 要能回到上一个稳定状态重新规划,而不是硬着头皮往下走。

2.3 状态表示与路径剪枝

Agent 每执行一步,界面状态就会变化。如果不做剪枝,搜索空间会指数级膨胀。我们的做法是给每个状态算一个"哈希指纹":取 DOM 树的结构摘要 + 关键元素的文本内容 + 截图感知哈希,三者拼接后做 SHA1。

如果新状态的指纹在本次路径中已经出现过,说明形成了循环,直接剪掉。这个策略帮我们把平均路径长度从 47 步压缩到了 12 步。另外,对于"探索型"测试(没有明确目标,就是让 Agent 自己逛),我们会给每个状态打一个"新颖度分数",优先走没走过的分支,避免 Agent 在一个页面反复横跳。

状态表示还有一个细节:不要把时间戳、随机 ID 这类易变信息纳入指纹。我们一开始没注意,导致每次指纹都不一样,剪枝完全失效。后来加了一层正则过滤,把 UUID、时间戳、随机 token 都归一化成占位符,问题才解决。

3. 核心细节解析与实操要点

3.1 视觉定位的提示词设计与坐标映射

多模态模型输出坐标,最大的问题是"它说的坐标"和"实际屏幕坐标"对不上。我们试过直接让模型输出[x, y],结果偏差经常在 50 像素以上。后来改成让模型输出元素描述 + 相对位置,再由代码做映射,准确率大幅提升。

具体做法是:给模型一张标注了网格的截图(比如 10x10 的网格,每个格子标号 A1-J10),让它输出"目标元素在哪个格子"。然后代码根据格子编号反算实际坐标范围,再结合 DOM 投影的候选框做交集,最终确定点击点。

提示词模板大概长这样:

你是一个 UI 测试助手。当前截图已划分为 10x10 网格,行用 A-J 表示,列用 1-10 表示。 请根据以下目标,选择下一步应该操作的元素: 目标:{test_goal} 当前已执行步骤:{history} 请输出 JSON 格式: {"grid": "C5", "element_desc": "登录按钮", "action": "click", "reason": "..."}

这个设计的精妙之处在于:网格把连续坐标离散化了,模型不需要精确到像素,只需要判断"大概在哪个区域",容错率高了很多。实测下来,网格定位的 Top-1 准确率能到 82%,Top-3 能到 94%。

3.2 DOM 与视觉的融合对齐算法

融合对齐的核心是把 DOM 节点的边界框投影到截图坐标系。这里有几个坑:

  • 缩放比例:浏览器窗口大小和截图分辨率往往不一致,需要按devicePixelRatio做换算。
  • 滚动偏移:如果页面滚动了,DOM 的getBoundingClientRect()返回的是视口坐标,而截图可能是整页截图,需要加上scrollX/scrollY。
  • iframe 嵌套:跨 iframe 的坐标需要逐层累加偏移量。

我们的对齐算法伪代码如下:

def align_dom_to_screenshot(dom_nodes, screenshot_size, viewport_size, scroll_offset, dpr): scale_x = screenshot_size.width / viewport_size.width scale_y = screenshot_size.height / viewport_size.height aligned = [] for node in dom_nodes: rect = node.bounding_rect x = (rect.x + scroll_offset.x) * scale_x * dpr y = (rect.y + scroll_offset.y) * scale_y * dpr w = rect.width * scale_x * dpr h = rect.height * scale_y * dpr aligned.append({**node, "screen_box": (x, y, w, h)}) return aligned

对齐完成后,每个候选元素既有 DOM 属性(tag、text、role、enabled),又有屏幕坐标。Agent 决策时,视觉模型给出网格区域,代码找出与该区域重叠度最高的 DOM 节点,两者互相验证。如果视觉和 DOM 指向同一个元素,置信度拉满;如果冲突,则触发人工规则兜底。

3.3 动作空间的定义与约束

Agent 能做的动作必须严格定义,否则它会"发明"一些执行引擎不支持的操作。我们的动作空间包括:

动作类型参数说明
clickelement_id点击指定元素
inputelement_id, text向输入框写入文本
scrolldirection, distance滚动页面
waitcondition, timeout等待条件满足
assertelement_id, expected断言元素状态
back无返回上一页
screenshot无主动截图记录

每个动作执行后,Agent 会收到一个"执行结果"反馈:成功/失败、界面是否变化、是否有报错弹窗。这个反馈会作为下一步决策的输入。特别注意wait动作——很多测试失败是因为没等元素加载完就操作,我们强制要求 Agent 在点击后默认等待 500ms,或者等待某个 loading 指示器消失。

注意:input动作要先判断输入框是否已有内容。如果有,先执行清空(全选+删除),再输入新文本。这个细节不做,测试数据会脏得没法看。

4. 实操过程与核心环节实现

4.1 环境搭建与依赖准备

我们用的技术栈是 Python + Playwright + 一个多模态模型 API。Playwright 负责浏览器控制和 DOM 提取,模型负责视觉理解和决策。环境准备步骤如下:

pip install playwright pillow numpy opencv-python playwright install chromium

模型侧我们封装了一个统一的调用接口,支持传入截图和文本提示,返回结构化 JSON。这里不绑定具体模型,你可以用任何支持视觉输入的模型服务。

关键配置项:

CONFIG = { "screenshot_quality": 80, "grid_rows": 10, "grid_cols": 10, "max_steps_per_case": 30, "action_timeout_ms": 5000, "wait_after_action_ms": 500, "state_hash_filter": r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", }

max_steps_per_case是硬性熔断,防止 Agent 陷入死循环。30 步是我们根据历史用例统计出来的 P95 值,超过这个步数基本可以判定为异常。

4.2 单步决策循环的实现

Agent 的主循环逻辑是这样的:

def run_test_case(page, test_goal): history = [] visited_states = set() for step in range(CONFIG["max_steps_per_case"]): screenshot = capture_screenshot(page) dom_nodes = extract_dom(page) aligned = align_dom_to_screenshot(dom_nodes, screenshot, page) state_hash = compute_state_hash(dom_nodes, screenshot) if state_hash in visited_states: history.append({"action": "back", "reason": "state loop detected"}) page.go_back() continue visited_states.add(state_hash) decision = call_multimodal_model(screenshot, test_goal, history, aligned) result = execute_action(page, decision, aligned) history.append({"decision": decision, "result": result}) if check_goal_reached(page, test_goal): return {"status": "passed", "steps": step + 1, "history": history} return {"status": "timeout", "steps": CONFIG["max_steps_per_case"], "history": history}

这个循环里,compute_state_hash和check_goal_reached是两个需要重点打磨的函数。前者决定剪枝效果,后者决定测试能否正确终止。

4.3 状态哈希的计算细节

状态哈希的输入有三个部分:DOM 结构摘要、关键文本内容、截图感知哈希。DOM 摘要我们只取可交互元素(button、input、a、select)的 tag + role + 文本前 20 字符,拼成一个字符串。文本内容取页面上所有可见文本的前 500 字符。截图感知哈希用 dHash 算法,把图片缩到 9x8 灰度图,算相邻像素差值得到 64 位指纹。

三者拼接后再做 SHA1,得到最终状态哈希。实测这个组合对"页面轻微变化"(比如时间显示更新)不敏感,但对"实质变化"(比如弹窗出现、列表刷新)能准确区分。

4.4 目标校验的实现方式

check_goal_reached我们用了两种模式:

模式一:断言驱动。测试用例里预定义了断言条件,比如"页面上出现文本'账号已锁定'"。Agent 每步执行后,代码检查断言是否满足。

模式二:模型判断。对于没有明确断言的探索型测试,把当前截图和测试目标一起丢给模型,问它"目标是否已达成"。模型返回 yes/no 加理由。

两种模式结合使用:有断言优先用断言,断言不明确时用模型兜底。模型判断的准确率大概在 88% 左右,所以关键用例还是建议写明确断言。

4.5 一次完整测试路径的实录

拿"登录失败三次锁定账号"这个用例举例,Agent 的实际执行路径如下:

  1. 初始状态:登录页,两个输入框(用户名、密码),一个登录按钮。
  2. 第 1 步:视觉模型识别到用户名输入框在网格 B3,DOM 对齐确认是#username,执行 input 动作。
  3. 第 2 步:识别密码框在 B5,执行 input 动作,输入错误密码。
  4. 第 3 步:识别登录按钮在 C7,执行 click。
  5. 第 4 步:等待 500ms,检测到错误提示"用户名或密码错误",状态哈希变化。
  6. 第 5-10 步:重复上述流程两次。
  7. 第 11 步:第三次点击登录后,检测到提示变为"账号已锁定,请 15 分钟后重试"。
  8. 第 12 步:断言匹配,测试通过。

整个过程 12 步,耗时约 18 秒。如果用传统脚本,这个用例大概 8 步就能跑完,但传统脚本在页面改版后需要人工维护,而 Agent 方案在页面结构变化后仍能自适应——这是我们愿意接受额外步数开销的原因。

5. 常见问题与排查技巧实录

5.1 视觉模型定位漂移怎么办

这是最高频的问题。表现是:模型说点击 C5 区域,但实际目标元素在 D5。排查思路分三步:

  • 检查网格标注是否清晰:网格线太细或颜色太浅,模型可能看不清。我们把网格线加粗到 2px,用半透明红色,准确率提升了 7%。
  • 检查截图分辨率:分辨率太低(比如低于 1280x720),模型难以分辨小元素。建议截图宽度不低于 1440px。
  • 检查元素是否被遮挡:如果有弹窗、浮层遮挡了目标元素,模型看到的是遮挡物。这种情况需要先让 Agent 处理遮挡。

实操心得:在提示词里加一句"如果目标元素被遮挡,请先输出关闭遮挡物的动作",能显著减少这类问题。

5.2 Agent 陷入循环怎么破

循环的典型表现是:Agent 在 A 页面和 B 页面之间反复跳转,或者反复点击同一个按钮。我们的排查清单:

现象可能原因解决方式
反复点击同一按钮点击后界面无变化,Agent 以为没点到增加点击后的状态变化检测,无变化则标记该动作失败
A/B 页面反复跳状态哈希未正确区分页面检查哈希是否包含足够区分信息
反复滚动目标元素在视口外,Agent 找不到增加"滚动后重新截图"逻辑
反复输入输入框内容未正确清空检查 input 动作的清空逻辑

我们后来加了一个"动作频率惩罚":如果某个动作在最近 5 步内执行过 3 次以上,强制 Agent 换一个动作。这个简单的规则把循环率从 15% 降到了 3% 以下。

5.3 动态加载和异步渲染的处理

现代前端大量使用异步加载,Agent 经常在元素还没渲染出来时就尝试操作。我们的处理策略是:

  • 显式等待:点击后默认等待 500ms,如果检测到 loading 指示器,延长到指示器消失。
  • 重试机制:动作失败后自动重试 2 次,每次间隔 300ms。
  • 超时熔断:单个动作超过 5 秒未完成,标记为失败并进入下一步。

这里有个细节:等待时间不能设太长,否则测试整体耗时爆炸。我们统计过,500ms + 重试 2 次的组合,能覆盖 90% 的异步场景,同时单步平均耗时控制在 1.2 秒以内。

5.4 模型调用成本与性能优化

多模态模型调用不便宜,每一步都调一次,一个用例跑下来成本可观。我们的优化手段:

  • 缓存:相同截图 + 相同目标的决策结果缓存 5 分钟,避免重复调用。
  • 降采样:截图先缩到 1440px 宽再传给模型,减少 token 消耗。
  • 规则前置:简单的动作(比如"点击页面上唯一可见的按钮")用规则直接处理,不走模型。
  • 批量决策:对于连续的表单填写,让模型一次性输出多个输入动作,减少调用次数。

这套组合拳下来,单用例平均模型调用次数从 12 次降到了 4.3 次,成本降低了约 64%。

5.5 测试结果的可信度保障

Agent 自主决策最大的风险是"它说通过了,但其实没测到"。我们的保障措施:

  • 双模型交叉验证:关键断言用两个不同的模型分别判断,结果一致才认可。
  • 截图存档:每一步的截图都存档,失败用例可回溯。
  • 人工抽检:每天随机抽 5% 的通过用例人工复核,发现误判及时调整提示词。
  • 断言覆盖率统计:统计有多少用例是"模型判断通过"而非"断言通过",这个比例超过 30% 就要警惕。

最后分享一个我个人的体会:多模态 Agent 做 UI 测试,现阶段最适合的场景是回归测试和探索性测试,而不是替代所有传统脚本。它的价值在于覆盖那些"写脚本成本太高、不写又不放心"的长尾场景。把它当成测试团队的一个补充工具,而不是银弹,心态会平和很多,落地也会顺利很多。

相关新闻

基于YOLOv12的冰箱内食物识别检测系统:从YOLOv8到v12的优化实践

基于YOLOv12的冰箱内食物识别检测系统:从YOLOv8到v12的优化实践

每个在家做饭的人,应该都有过这种经历:翻遍冰箱找不到那半盒牛奶,等找到时它已经成了冰箱深处一罐散发着存在感的“历史文物”。我决定做一个能自动看懂冰箱内容的系统,最终交付的东西,就是标题里那套“基于深度学习YO…

2026/10/20 9:11:59 阅读更多 →
JEB安卓逆向实战:从APK解析到脚本化批量分析

JEB安卓逆向实战:从APK解析到脚本化批量分析

做安卓逆向这几年,工具换了一茬又一茬,但JEB一直稳稳躺在我的主力工具列表里。可能有人会说,jadx够用了,还要JEB干什么?真到了分析恶意样本、追踪协议算法、调smali数据流的时候,你才会明白JEB这套东西的价…

2026/10/20 9:11:59 阅读更多 →
CMake集成MuJoCo:C++动力学计算的最小工程实践

CMake集成MuJoCo:C++动力学计算的最小工程实践

简介:面向机器人、运动控制与强化学习领域的 C 开发者,这是一套以 CMake 搭建 MuJoCo 工程并执行基础动力学计算的完整示例包。包内展示了从 CMakeLists 配置、XML/URDF 模型定义到加载模型、初始化数据并反复调用 mj_step 推进仿真的关键流程&#xff0…

2026/10/20 9:11:59 阅读更多 →

最新新闻

为什么一降重AI率就升高?手动降重越改越像AI,降重降AI一起做才是正解!

为什么一降重AI率就升高?手动降重越改越像AI,降重降AI一起做才是正解!

为什么一降重AI率就升高?手动降重越改越像AI,降重降AI一起做才是正解! 为什么一降重AI率就升高?不少同学都碰到过:查重报告上的红色总算改少了,再送去做AIGC检测,AI率反而比改之前还高。明明每…

2026/10/20 10:44:37 阅读更多 →
基于Springboot大学生就业服务平台【附源码+文档】

基于Springboot大学生就业服务平台【附源码+文档】

/* 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:44:37 阅读更多 →
Node.js部署实战:从环境配置、PM2进程守护到日志排错的完整指南

Node.js部署实战:从环境配置、PM2进程守护到日志排错的完整指南

开头:真香的是开发,想砸电脑的是部署我当初也是冲着 Node.js 那套开发体验去的,写接口、做工具脚本、甚至给团队搭个内部面板,简直行云流水。直到第一次正儿八经把服务部署到服务器上,才发现前端的爽和后端的痛之间&am…

2026/10/20 10:44:37 阅读更多 →
论文AI率和重复率怎么一起降下来?降重降AI几分钟就能一起完成!

论文AI率和重复率怎么一起降下来?降重降AI几分钟就能一起完成!

论文AI率和重复率怎么一起降下来?降重降AI几分钟就能一起完成! 论文AI率和重复率怎么一起降下来,是很多同学交稿前最头疼的事。论文AI率和重复率两个数字同时摆在报告上,先压哪个都不对:刚把AI率压下去,查…

2026/10/20 10:44:37 阅读更多 →
AgentScope实践:用标准化Msg协议搭建多智能体协作系统

AgentScope实践:用标准化Msg协议搭建多智能体协作系统

上周我在重构一个内部客服项目的时候,终于体会到“多智能体”和“多个会说话的模块”之间那条天堑。单机脚本里我写了三百多行状态机,让一个主管Agent给三个执行Agent派活,结果一个Agent因为提示词里写了句“你继续分析”,就带着上…

2026/10/20 10:44:37 阅读更多 →
如何 5 步把游戏手柄映射为键盘鼠标?AntiMicroX 完整指南

如何 5 步把游戏手柄映射为键盘鼠标?AntiMicroX 完整指南

如何 5 步把游戏手柄映射为键盘鼠标?AntiMicroX 完整指南 【免费下载链接】antimicrox Graphical program used to map keyboard buttons and mouse controls to a gamepad. Useful for playing games with no gamepad support. 项目地址: https://gitcode.com/Gi…

2026/10/20 10:43:36 阅读更多 →

日新闻

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实现详解

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

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

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

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

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

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

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在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 阅读更多 →