通过Browser Harness自动复现前端bug、执行用例

SKILL开发
开发 未标注权限 许可证不明确
安装这个技能

前端质量保障自动化 Agent。输入 Bug 描述 → 自动复现、采集网络/控制台证据、定位根因、给出修改建议。输入测试步骤 → LLM 翻译为操作序列 → 截图+坐标点击穿透 Shadow DOM → 逐条执行并生成通过率报告。输入已修复 Bug ID → 重放复现步骤 → 修复前后对比 → 判定通过/失败/部分。输入 Git Diff → 构建依赖图 → 自动发散验证受影响场景。基于 Browser Harness (CDP) + LLM 视觉定位 + 源码交叉分析。框架兼容输入 (React/Vue),异常保护,变异重试。禁止 Playwright。

一句话选型结论

按资料字段整理

通过Browser Harness自动复现前端bug、执行用例是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_1417f0ce/qa-agent
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_1417f0ce仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791632501
正文体量20.4 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
通过Browser Harness自动复现前端bug、执行用例当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

20.4 KB
模型实际读到的说明书原文,来自技能仓库

QA Agent — 前端质量保障自动化 (v1.1)

输入即执行: Bug 描述 → 自动复现 + 定位。测试步骤 → 自动执行 + 报告。代码变更 → 影响分析 + 发散验证。

基于 Browser Harness (CDP 协议) 驱动真实浏览器,LLM 将自然语言翻译为操作序列,截图 + 坐标点击穿透 Shadow DOM。


前置条件与强制约束

约束 说明
✅ 必须使用 Browser Harness 所有浏览器操作通过 CDP 协议执行,禁止 Playwright/Puppeteer/Selenium
🔧 未安装时询问用户 browser-harness --doctor 失败 → 停止 → 提供安装文档 → 询问是否自动安装
🚫 禁止替用户操作 Chrome 设置 daemon 连接失败时引导用户自己打开 chrome://inspect/#remote-debugging 并点击 Allow。Agent 端浏览器无法控制用户 Chrome
📂 有源码时自动增强 检测到 src/ 或 package.json → 自动启用源码交叉分析 (同 page-spec-generator v3.3 +src 插件)

首次使用验证

在正式开始前,先跑 5 行脚本确认环境正常:

browser-harness <<'PY'
new_tab("https://www.baidu.com")
print(page_info())
PY
验证项 通过标志
daemon + CDP 连接 page_info() 返回 dict 非报错
页面导航 url 字段为目标 URL
错误信息 原因 解决
fatal: CDP WS handshake failed Chrome 远程调试未授权 chrome://inspect 重勾 Allow
Connection refused Chrome 未启动 启动 Chrome 或 Way 2 启动: chrome.exe --remote-debugging-port=9222 --user-data-dir=C:\temp\chrome-dev 然后 export BU_CDP_URL=http://127.0.0.1:9222
TimeoutError 网络不通 换 https://www.baidu.com (国内最快)
Target ... not found 标签页失效 调用 ensure_real_tab() 恢复

四个核心能力

用户输入
  │
  ├── "复现这个 bug: {描述}"
  │     → 🔍 流水线 §Q: Bug 复现 + 根因定位
  │
  ├── "验证 {BUG-123} 修好了没"
  │     → ✅ 流水线 §R: 回归验证
  │
  ├── "分析这次修改影响了哪些功能"
  │     → 🌐 流水线 §I: 影响发散
  │
  └── "执行这些测试步骤: {步骤列表}"
        → 🤖 流水线 §T: 用例自动执行

流水线 §Q: Bug 复现 + 根因定位

整体流程

Bug 描述 (自然语言)
     │
 Q0 ── LLM 结构化提取
     │   输入: 任何形式的 bug 描述 (Jira / Slack / GitHub Issue / 口头)
     │   输出: { url, steps[], expectedBehavior, actualBehavior, missingInfo[] }
     │
 Q1 ── 操作序列规划
     │   每个 step → LLM 翻译为 Browser Harness 原子操作链
     │   操作类型: navigate / click / input / select / scroll / hover / wait
     │
 Q2 ── 逐步执行 + 三层日志
     │   for each action:
     │     截图(执行前) → 执行 → 截图(执行后)
     │     记录: DOM 变化 / 网络请求 / 控制台错误
     │
 Q3 ── 结果判断
     │   实际行为 vs 预期行为 → 复现成功 / 失败
     │   未复现 → 3 次变异重试 (换视口/换数据/节流/清缓存)
     │
 Q4 ── 根因定位
     │   控制台错误 → 前端代码定位
     │   网络错误 → 后端 API 定位
     │   视觉异常 → 渲染问题定位
     │   输出: 层级 (前端/后端/数据) + 置信度 + 可疑文件
     │
 Q5 ── 修改建议
       输出: 可疑代码位置 + 修复方向 + 临时规避方案

Q0: LLM 结构化提取

def parse_bug_report(user_input):
    """从自然语言 bug 描述中提取结构化信息。"""
    prompt = f"""
    从以下 bug 描述中提取结构化信息。无法确定的字段填 null。

    Bug 描述: {user_input}

    返回 JSON:
    {{
      "url": "起始页面 URL",
      "title": "一句话标题",
      "severity": "critical / major / minor",
      "steps": [
        {{ "order": 1, "action": "navigate / click / input / select", 
           "target": "操作目标", "value": "输入值" }}
      ],
      "expectedBehavior": "预期行为",
      "actualBehavior": "实际行为",
      "errorInfo": {{ "consoleError": "", "networkError": "", "visualGlitch": "" }},
      "missingInfo": ["缺失的关键信息"]
    }}
    """
    return llm_json(prompt)

缺失信息处理:
| 缺失项 | 处理 |
|--------|------|
| URL | 尝试从文本提取链接;无则询问用户 |
| 登录态 | 先以未登录尝试;受阻则暂停提示用户登录 |
| 操作目标模糊 | 截图 + LLM 视觉识别找最可能元素 |
| 前置数据 | 尝试自动创建,复杂则标记需人工准备 |

Q1: 操作序列规划

每个步骤翻译为 Browser Harness 原子操作:

"打开 {url}"       → new_tab(url) → wait_for_load() → wait_for_network_idle()
"点击 {目标}"      → capture_screenshot() → LLM 视觉定位坐标 → click_at_xy(x, y)
"输入 {内容}"      → capture_screenshot() → LLM 定位输入框 → click_at_xy(x, y) → fill value
"选择 {选项}"      → capture_screenshot() → LLM 定位选项 → click_at_xy(x, y)
"验证 {内容}存在"  → capture_screenshot() → LLM 检查文本

视觉定位 Prompt:

这张截图是一个网页。请找到 "{target}" 的像素坐标。
返回: {{ "x": 中心x, "y": 中心y, "confidence": 0-1, "reason": "依据" }}
找不到: {{ "x": null, "y": null, "confidence": 0, "reason": "原因" }}

Q2: 逐步执行

前置: 注入证据采集器(在执行任何操作之前):

def inject_evidence_collectors():
    """在页面中注入控制台错误和网络请求拦截器。"""
    js("""
    window.__qa_console_errors = [];
    window.addEventListener('error', function(e) {
        window.__qa_console_errors.push({
            type: 'error', message: e.message,
            filename: (e.filename||'').split('/').pop(),
            line: e.lineno, timestamp: Date.now()
        });
    });
    window.addEventListener('unhandledrejection', function(e) {
        window.__qa_console_errors.push({
            type: 'unhandledrejection',
            message: String(e.reason).substring(0, 500),
            timestamp: Date.now()
        });
    });

    window.__qa_network_log = [];
    var _origFetch = window.fetch;
    window.fetch = async function(url, opts) {
        var start = Date.now();
        var reqUrl = typeof url === 'string' ? url : (url.url || '');
        var reqMethod = (opts && opts.method) || 'GET';
        var reqBody = '';
        try { reqBody = (opts && opts.body) ? String(opts.body).substring(0, 1000) : ''; } catch(e) {}
        try {
            var resp = await _origFetch.apply(this, arguments);
            var clone = resp.clone();
            var respBody = await clone.text().catch(function(){ return ''; });
            window.__qa_network_log.push({
                url: reqUrl, method: reqMethod, status: resp.status,
                duration: Date.now() - start, requestBody: reqBody,
                responseBody: respBody.substring(0, 1000), timestamp: Date.now()
            });
            return resp;
        } catch(e) {
            window.__qa_network_log.push({
                url: reqUrl, method: reqMethod, error: String(e).substring(0, 300),
                duration: Date.now() - start, timestamp: Date.now()
            });
            throw e;
        }
    };
    """)

def get_console_errors():
    return json.loads(js("JSON.stringify(window.__qa_console_errors || [])"))

def get_network_errors():
    all_logs = json.loads(js("JSON.stringify(window.__qa_network_log || [])"))
    return [r for r in all_logs if r.get('status', 0) >= 400 or r.get('error')]

执行循环 (含异常保护):

def execute_action_plan(plan):
    inject_evidence_collectors()
    trace = []

    for i, action in enumerate(plan):
        step_entry = {"step": i, "action": action}

        try:
            # 执行前快照
            step_entry["before"] = {
                "url": js("window.location.href"),
                "shot": f"bug_step{i}_before"
            }
            try:
                capture_screenshot(step_entry["before"]["shot"])
            except Exception:
                step_entry["before"]["shot"] = "screenshot_failed"

            # 执行操作
            if action["action"] == "navigate":
                new_tab(action["url"])
                wait_for_load()
                wait_for_network_idle(timeout=15, idle_ms=1500)
            elif action["action"] == "click":
                coord = llm_visual_locate(action["target"], step_entry["before"]["shot"])
                if coord.get("x") is not None:
                    click_at_xy(coord["x"], coord["y"])
                else:
                    step_entry["error"] = f"visual locate failed: {coord.get('reason','')}"
            elif action["action"] == "input":
                coord = llm_visual_locate(action["target"], step_entry["before"]["shot"])
                if coord.get("x") is not None:
                    click_at_xy(coord["x"], coord["y"])
                    value = json.dumps(action['value'])
                    js(f"""
                        var el = document.activeElement;
                        var setter = Object.getOwnPropertyDescriptor(
                            window.HTMLInputElement.prototype, 'value'
                        ).set;
                        setter.call(el, {value});
                        el.dispatchEvent(new Event('input', {{ bubbles: true }}));
                        el.dispatchEvent(new Event('change', {{ bubbles: true }}));
                    """)
                else:
                    step_entry["error"] = f"visual locate failed: {coord.get('reason','')}"

            time.sleep(1.5)

            # 执行后快照
            step_entry["after"] = {
                "url": js("window.location.href"),
                "shot": f"bug_step{i}_after"
            }
            try:
                capture_screenshot(step_entry["after"]["shot"])
            except Exception:
                step_entry["after"]["shot"] = "screenshot_failed"

            step_entry["consoleErrors"] = get_console_errors()
            step_entry["networkErrors"] = get_network_errors()

        except Exception as e:
            step_entry["error"] = f"{type(e).__name__}: {str(e)[:300]}"
            step_entry["partial"] = True

        trace.append(step_entry)

        # 关键操作失败则停止后续步骤
        if step_entry.get("error") and action["action"] in ("navigate", "click"):
            break

    return trace

Q3: 结果判断

def judge_reproduction(trace, expected, actual_described):
    last = trace[-1]

    # 判断 1: 控制台错误
    has_error = len(last["consoleErrors"]) > 0

    # 判断 2: 页面白屏 (body 文本极少)
    is_white_screen = js("document.body.innerText.length") < 100

    # 判断 3: LLM 视觉比对
    # 将实际截图与 Bug 描述中的 actualBehavior 比对,而非 expected
    llm_result = llm_compare(
        actual_screenshot=last["after"]["shot"],
        described_behavior=actual_described,
        console=last["consoleErrors"]
    )

    # reproduced = 实际表现匹配 Bug 描述中的异常行为
    if has_error or is_white_screen or llm_result.matches:
        return {"reproduced": True, "confidence": "high", "evidence": trace}

    # 未复现 → 变异重试 (最多 3 次)
    for i, variant in enumerate(generate_variants()):
        if i >= 3:
            break
        retry_trace = execute_with_variant(plan, variant)
        if judge_reproduction(retry_trace, expected, actual_described)["reproduced"]:
            return {"reproduced": True, "confidence": "medium", 
                    "variant": variant, "evidence": retry_trace}

    return {"reproduced": False, "confidence": "low"}

变异策略: 移动端视口 / 不同输入数据 / 加速操作 / 网络节流 / 清除缓存 / 不同登录态。最多 3 次。

Q4-Q5: 根因定位 + 修改建议

分层定位:

控制台错误
  ├─ "Cannot read property 'X' of undefined"
  │     → API 响应缺少字段 → 前端未做空值保护
  │     → +src:api 定位 API 调用代码
  │     → 建议: 前端加 ?. 空值保护
  │
  ├─ HTTP 4xx/5xx
  │     → 从 Performance API 提取失败请求 URL
  │     → +src:api 定位调用方
  │     → 建议: 检查 API 参数/认证/可用性
  │
  └─ 无错误但页面异常
        → 截图对比 (正常 vs 异常)
        → LLM 视觉分析差异区域
        → 建议: 检查组件渲染条件 / CSS 布局

流水线 §R: 回归验证

R0 ── 读取原始 Bug 的复现步骤 (来自 Q2 的 trace)
  │
R1 ── 在修复后的页面上重放完全相同的操作序列
  │   使用保存的坐标/输入值
  │   若 DOM 变化导致坐标失效 → LLM 重新视觉定位
  │
R2 ── 对比修复前后
  │   before.png vs after.png (截图对比)
  │   控制台对比 (JS 异常是否消失)
  │   网络对比 (错误 API 是否恢复正常)
  │
R3 ── 判定
      ✅ 通过: 原异常消失 + 核心功能正常
      ❌ 失败: 原异常仍存在
      ⚠️ 部分: 原异常消失但出现新异常

流水线 §I: 影响发散

I0 ── 输入: 修改文件列表 (git diff --name-only) + 基础 URL
  │
I1 ── 构建影响图 (需要源码)
  │   对每个修改文件:
  │     组件: 提取 Props → 找所有引用该组件的父组件
  │     API:   提取函数名 → 找所有调用该 API 的页面
  │     Store: 提取 state 字段 → 找所有读取该字段的组件
  │     路由:  修改的路由 → 提取关联导航入口
  │
I2 ── 生成验证清单
  │   每组受影响场景 → 一个验证任务
  │   按风险排序: 直接引用 > 间接引用 > 同模块
  │
I3 ── 自动探索验证
  │   for each task:
  │     LLM 翻译 → Browser Harness 执行 → 截图 → 比对
  │     异常 (报错/白屏/样式错乱) → 标记风险点
  │
I4 ── 发散验证报告
      影响范围: {N} 场景需验证
      自动验证: {M} 通过 / {K} 需人工
      风险点: 异常截图 + 说明

流水线 §T: 用例自动执行

T0 ── 解析测试步骤
  │   支持格式:
  │     Gherkin:   Given/When/Then
  │     编号列表:  1. 打开页面 2. 输入xxx 3. 验证xxx
  │     自然语言:  "打开登录页,输入admin/admin,点击登录,应该跳转首页"
  │   LLM 提取: 每步的操作 + 预期结果
  │
T1 ── 逐步执行
  │   for each step:
  │     截图(执行前)
  │     LLM 翻译 → Browser Harness 操作 → 执行
  │     截图(执行后)
  │     若含 "验证/检查/确认/应该":
  │       LLM 判断页面文本/URL/元素是否满足预期
  │       记录: ✅ 通过 / ❌ 失败 (附差异说明)
  │
T2 ── 异常处理
  │   操作失败 → 重试 1 次 (等待更久)
  │   仍失败 → 标记 ❌ 阻塞,跳过后续步骤
  │
T3 ── 生成报告

| 步骤 | 操作 | 预期 | 实际 | 截图 | 结果 |
|------|------|------|------|------|------|
| 1 | 打开 /login | 登录页 | 正常 | [图] | ✅ |
| 2 | 输入 admin/admin | 可提交 | 按钮可点击 | [图] | ✅ |
| 3 | 点击登录 | 跳转首页 | URL 变为 /home | [图] | ✅ |

通过率: 3/3 (100%)

LLM 翻译层设计

分层调用 (4 个 LLM 角色)

LLM-1 (结构化提取): Bug描述 → StructuredBug JSON
LLM-2 (操作规划):   Step[] → ActionPlan (操作链)
LLM-3 (视觉定位):   截图 + target → {x, y, confidence}
LLM-4 (断言判断):   截图 + 预期 → {pass: bool, reason: str}

分层的好处: 每个 LLM 只做一件事,Prompt 更短更精确。

视觉定位为首选

方式 优点 缺点 适用场景
截图+坐标 穿透 Shadow DOM、不受 DOM 结构影响 依赖视觉模型、布局变化需重定位 默认首选
CSS 选择器 精确、快速 DOM 变化即失效 有 data-testid 时
文本匹配 直观稳定 同文本多元素需消歧 唯一文本时

操作失败自适应

LLM-3 返回 confidence < 0.7
  → 不执行,先消歧:
    1. 滚动页面不同区域再截图
    2. 尝试不同描述词
    3. 仍低置信度 → 标记需人工介入

输出产物

能力 产物 内容
Bug 复现 bug-{id}-analysis.md 复现证据 (截图+日志) + 根因定位 + 修改建议
回归验证 bug-{id}-regression.md 修复前后对比 + 通过/失败/部分 判定
影响发散 impact-{commit}-report.md 影响场景清单 + 自动验证结果 + 风险点
用例执行 testrun-{name}-report.md 步骤×结果矩阵 + 截图 + 通过率
共用 screenshots/{run-id}/ 每步操作前后截图
共用 traces/{run-id}/ 网络日志 + 控制台日志 + DOM 快照

与 page-spec-generator 的关系

维度 page-spec-generator QA Agent
定位 探索未知站点 → 生成规格文档 针对已知问题/用例 → 定向验证
输入 URL Bug 描述 / 测试步骤 / Git Diff
驱动 8 阶段结构化流水线 LLM 翻译自然语言 → 操作序列
输出 14/8 章规格文档 步骤×结果矩阵 QA 报告
共享 Browser Harness CDP Browser Harness CDP
共享 +src 源码插件 v3.3 +src 源码插件 v3.3

Browser Harness 操作注意事项

问题 对策
截图超时导致流程中断 截图包裹 try/except,失败降级为文本断言,不抛异常
js() 无法引用 Python 变量 js() 在 Chrome 上下文执行,需传参用 f-string: js(f"el.value = {json.dumps(val)}")
CDP 调用无自动重试 capture_screenshot()/click_at_xy() 失败时原文报告,不自动重试超 2 次
标签页意外切换 用户切标签/刷新/chrome-error:// 后 session 失效 → ensure_real_tab() 恢复
SPA 页面滚动不可靠 CDP mouseWheel 是 Chromium 已知 Bug。用 js("window.scrollBy(0,300)") 替代 CDP scroll

常见问题

Q: 视觉定位找不到目标元素怎么办?

  1. 滚动画面的不同区域再截图重试
  2. 尝试不同描述词 ("微信支付" → "WeChat Pay" → "微信")
  3. 尝试 CSS 选择器 / 文本匹配降级
  4. 三次消歧失败 → 标记需人工介入

Q: Bug 描述信息不全怎么办?

先执行结构化提取 (Q0),将 missingInfo 列出。如果缺失的信息是关键前置条件 (URL、登录账号),暂停并询问用户。如果是次要信息 (浏览器版本),用默认值继续。

Q: 操作会产生不可逆后果怎么办?

提交订单、删除数据、发送请求等不可逆操作 → 执行前确认: "此操作将 {后果},是否继续?" 用户确认后才执行。

Q: 和 AgentiTest / e2e-test-agent 的区别?

  • 它们: 纯测试用例执行 (Given/When/Then → Playwright)
  • QA Agent: Bug 复现 + 回归验证 + 影响发散 + 用例执行,四种能力合一
  • QA Agent 独有: 源码交叉分析 (+src)、截图坐标点击 (穿透 Shadow DOM)、变异重试策略

版本

v1.1 — 基于 Browser Harness CDP + LLM 视觉定位 + page-spec-generator v3.4 源码插件。四个子流水线: Bug 复现 (Q)、回归验证 (R)、影响发散 (I)、用例执行 (T)。新增首次使用验证、操作注意事项、Way 2 支持、SPA 兼容性警告。

常见问题

5 问
答案由本站依据条目字段整理
通过Browser Harness自动复现前端bug、执行用例怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_1417f0ce/qa-agent。安装方式为未标注。装完刷新技能列表或重开客户端。

通过Browser Harness自动复现前端bug、执行用例支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

通过Browser Harness自动复现前端bug、执行用例权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

通过Browser Harness自动复现前端bug、执行用例收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

通过Browser Harness自动复现前端bug、执行用例用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。