Langflow 前端可访问性批量扫描实战:基于 IBM ACE 的 Python 路由扫描器 a11y_scan.py
本文围绕 Langflow 仓库中 .agents/skills/ibm-a11y-route-scan 技能文档展开,系统讲解如何使用仓库内置的 Python 脚本 a11y_scan.py 对 Langflow 前端路由进行批量可访问性(Accessibility,a11y)扫描:从扫描器调用与参数、路由清单(route manifest)选择、模态框状态(state)扫描,到 JSON/Markdown/HTML 三类报告的字段解读。读完本文,你可以独立对任意一组前端路由产出可访问性问题报告,并能结合源码理解扫描器如何注入 IBM ACE 引擎、等待网络静默、追踪 API 请求并输出结果。
技能定位:只报告、不修复
该技能文档 .agents/skills/ibm-a11y-route-scan/SKILL.md 明确界定了工具的边界:
- 扫描范围:只扫描每条路由“默认加载完成”后的 DOM 状态;需要扫描模态框等交互状态时,必须通过显式的 state 文件描述操作,而不是让扫描器自动乱点页面。
- 只报告(reports findings only):该工具不修改代码、不运行 Playwright/axe 测试套件,也不做正式的 Level 1 审计。需要写测试、做正式审计或 PR 级别修复时,应转向仓库中对应的其他技能文档(
ibm-a11y-testing-guide、ibm-a11y-level1-audit、ibm-a11y-pr-remediation)。
这与 src/frontend/tests/a11y/README.md 中描述的双层 a11y 体系一致:Playwright 用例(src/frontend/tests/a11y/ 目录)是 CI 回归宿主,由 GitHub Actions 工作流 a11y-scan.yml 驱动;而 scripts/a11y/a11y_scan.py 是“ad-hoc 路由/报告工具”,用于自定义路由批次、模态框状态或本地问题分诊时快速产出 Markdown/HTML 报告。
扫描器与调用方式
扫描基于 a11y_scan.py 实现。该脚本通过 Playwright 打开 Chromium,把从 CDN 拉取的 IBM ACE(Accessiblity Checker Engine,默认地址为 https://unpkg.com/accessibility-checker-engine@latest/ace.js,见源码 a11y_scan.py 第 22 行 的 DEFAULT_ACE_URL)注入页面,对当前 DOM 执行检查。技能文档给出的标准批量扫描命令如下:
uv run python scripts/a11y/a11y_scan.py \
--url http://localhost:3000 \
--routes-file scripts/a11y/a11y_routes.json \
--route-group static \
--out /tmp/langflow-a11y-report.json \
--markdown /tmp/langflow-a11y-report.md \
--html /tmp/langflow-a11y-report.html \
--timeout-ms 45000
前提是本机已启动 Langflow 前端(默认 http://localhost:3000),并通过 uv 提供 Python 运行环境。
脚本参数完整说明
技能文档列出的参数与源码 parse_args()(a11y_scan.py 第 45-82 行) 完全对应,合并整理如下(含源码中额外可见的 --ace-url 与 --browser-executable):
| 参数 | 说明 | 默认值 |
|---|---|---|
--url |
应用基础 URL,通常为 http://localhost:3000,也可传完整页面 URL(必填) |
— |
--routes-file |
路由清单 JSON 文件,建议使用 scripts/a11y/a11y_routes.json | 空 |
--route-group |
使用清单中的哪个分组 | static |
--routes |
逗号分隔的路由路径列表 | 空 |
--route |
单个路由路径,可重复使用以指定多个路由 | 空 |
--levels |
逗号分隔的问题级别:violation,potentialviolation,recommendation,manual |
violation |
--out |
JSON 报告输出路径 | a11y-scan-report.json |
--markdown |
可选的 Markdown 报告路径 | 空 |
--html |
可选的自包含 HTML 报告路径 | 空 |
--timeout-ms |
每条路由的超时时间(毫秒) | 30000 |
--quiet-ms |
扫描前的网络静默窗口(毫秒) | 1000 |
--states-file |
显式模态框/状态操作 JSON 文件 | 空 |
--headed |
有头模式,扫描时可见浏览器窗口 | 关闭 |
--ace-url |
自定义 ACE 引擎下载地址 | unpkg 上的最新版 ACE |
--browser-executable |
指定 Chrome/Chromium 可执行文件路径,也可用环境变量 PLAYWRIGHT_CHROMIUM_EXECUTABLE |
自动探测(含 macOS 常见浏览器路径,见 find_chromium_executable,第 164-174 行) |
路由来源的优先级可以从 main() 入口(第 959-964 行) 看出:--routes/--route 显式参数 > --routes-file 指定的清单分组 > state 文件中出现过的路由 > 仅扫描 --url 对应的路径。也就是说,命令行显式路由始终覆盖清单。
路由选择:以路由清单为唯一事实来源
技能文档要求把 scripts/a11y/a11y_routes.json 作为路由选择的 source of truth。该清单按语义划分为四个分组:
static:默认批量扫描目标,即“常规 CI/本地批次”。当前清单包含 13 条静态路由,覆盖/flows、/components、/mcp、/assets/files、/assets/knowledge-bases以及/settings/下的 8 个设置页(general、global-variables、model-providers、db-providers、mcp-servers、mcp-client、api-keys、shortcuts、messages 等)。每条条目除id/path/surface外,还带有ready就绪检查(如mainpage_titletestid、设置页的settings_menu_header),供 CI 的 Playwright 用例在扫描前确认页面真正渲染完成。dynamic:需要真实 ID 才能扫描的路由模板,包括/flow/:id/(流程编辑器)、/playground/:id/(要求流程access_type为 PUBLIC 的共享 playground 页)、/assets/knowledge-bases/:sourceId/chunks(知识库源分块页)。文档明确要求:先从上载好的应用、API 响应或现有测试数据中取得真实 ID,替换占位符后再扫描。gated:需要特定认证状态的路由,如/login、/signup、/login/admin。清单注明这些页面在已登录时会重定向到/flows,因此必须在“未登录/禁用自动登录”模式下扫描。excluded:被默认批次排除的别名与重定向,例如/(运行时重定向到/flows)、/all(同一 HomePage 表面)、/assets(重定向到/assets/files)、各folder/:folderId变体(同一列表页仅数据不同),以及兜底*路由(重定向链最终落在/flows)。
清单顶部的 assumptions 字段还显式记录了扫描假设:BASENAME 为空(路由以 / 开头)、ENABLE_CUSTOM_PARAM 为 false(租户前缀变体未激活)、ENABLE_FILE_MANAGEMENT 与 ENABLE_KNOWLEDGE_BASES 均为 true(因此 /assets 与知识库路由生效)。这些假设决定了清单适用的部署前提。
常规清单驱动命令(技能文档的 Common manifest-backed command):
uv run python scripts/a11y/a11y_scan.py \
--url http://localhost:3000 \
--routes-file scripts/a11y/a11y_routes.json \
--route-group static \
--out /tmp/langflow-a11y-static.json \
--markdown /tmp/langflow-a11y-static.md \
--html /tmp/langflow-a11y-static.html
配套说明文档 scripts/a11y/a11y_scan_routes.md 进一步强调:不要在 Markdown 中手工维护路由列表,增删扫描目标应只改 JSON 清单,因为 Python 扫描器、Playwright a11y 用例与 HTML 报告都读取同一份清单。
常见扫描示例
技能文档给出了四类典型用法,全部可直接复制运行:
1. 扫描单条路由(最小化输出,只产 JSON):
uv run python scripts/a11y/a11y_scan.py \
--url http://localhost:3000 \
--route /flows \
--out /tmp/langflow-a11y-flows.json
2. 扫描清单中的多条路由(标准 static 批次,三格式输出):
uv run python scripts/a11y/a11y_scan.py \
--url http://localhost:3000 \
--routes-file scripts/a11y/a11y_routes.json \
--route-group static \
--out /tmp/langflow-a11y-report.json \
--markdown /tmp/langflow-a11y-report.md \
--html /tmp/langflow-a11y-report.html
3. 扫描比 violation 更宽的级别(加入潜在违规与建议项):
uv run python scripts/a11y/a11y_scan.py \
--url http://localhost:3000 \
--routes-file scripts/a11y/a11y_routes.json \
--route-group static \
--levels violation,potentialviolation,recommendation \
--out /tmp/langflow-a11y-expanded.json
4. 路由 + 模态框状态扫描(通过 --states-file 描述显式交互):
uv run python scripts/a11y/a11y_scan.py \
--url http://localhost:3000 \
--states-file /tmp/langflow-a11y-states.json \
--out /tmp/langflow-a11y-modal-report.json \
--markdown /tmp/langflow-a11y-modal-report.md \
--html /tmp/langflow-a11y-modal-report.html \
--timeout-ms 45000
State 文件格式与受支持的操作
--states-file 的结构是一个 JSON 数组,每个条目包含 route 和 states 列表;每个 state 有 name 以及打开(open)/关闭(close)两组动作序列。技能文档给出的示例(在“全局变量”设置页打开新建全局变量模态框):
[
{
"route": "/settings/global-variables",
"states": [
{
"name": "new-global-variable-modal",
"open": [
{ "click": "[data-testid='api-key-button-store']" },
{ "waitFor": "[role='dialog']" }
],
"close": [
{ "press": "Escape" },
{ "waitForHidden": "[role='dialog']" }
]
}
]
}
]
受支持的 10 种 state 动作及其源码映射(run_action(),a11y_scan.py 第 286-316 行):
{ "click": "<css selector>" }:点击第一个匹配元素;{ "clickText": "<visible text>" }:按可见文本(精确匹配)点击;{ "clickRole": { "role": "button", "name": "Create" } }:按 ARIA role + 可访问名点击;{ "fill": { "selector": "<css selector>", "value": "text" } }:向输入框填充文本;{ "press": "Escape" }:向页面按键;{ "press": { "selector": "<css selector>", "key": "Enter" } }:对指定元素按键;{ "waitFor": "<css selector>" }:等待元素可见;{ "waitForHidden": "<css selector>" }:等待元素隐藏;{ "waitForText": "<visible text>" }:等待可见文本出现;{ "wait": 500 }:固定等待毫秒数。
从源码实现看,每个动作执行后都会调用 wait_for_settled_network(第 177-189 行):持续检查同源非静态资源请求,直到“无在途请求且连续 --quiet-ms 毫秒网络静默”才认为页面稳定,然后才执行 ACE 检查。这解释了为什么扫描动态数据丰富的页面时通常需要更大的 --timeout-ms。
扫描器对状态的处理也值得注意:open 动作完成后会记录 phase: "open" 的结果并附加模态框诊断(当前可见的 [role="dialog"] 数量、焦点是否位于对话框内、打开前的活动元素);close 动作完成后记录 phase: "closed" 的结果,但关闭阶段不输出 issues(见 scan_route() 第 472-489 行 中 closed 阶段固定 issues: []),主要用于验证对话框能正确关闭与诊断状态。
报告格式与字段解读
扫描器总是写出 JSON(--out),Markdown 与 HTML 为可选展示格式。技能文档给出的选择建议:用 JSON 获取精确数据、用 Markdown 贴 PR 评论或 issue、用 HTML 做可浏览报告。
汇总时需要覆盖的信息
- 报告文件路径,以及生成的 Markdown/HTML 路径;
- 问题总数(
totalIssues); - 每条路由的问题数、API 请求数、请求失败数;
- 高频 rule ID(top rules)。
报告字段
顶层字段:generatedAt、url、routes、reportLevels、totalIssues、results(见 main() 报告组装,第 1002-1009 行)。每条 results[] 记录包含:
results[].route:路由路径;results[].state:状态名,基础加载态为base,模态框态为 state 文件中的name;results[].phase:loaded(基础加载)、open(模态框打开)、closed(模态框关闭);results[].apiRequests:扫描期间观察到的同源 API 请求(method/url/status);results[].requestFailures:失败的请求及失败原因;results[].diagnostics:模态框诊断(dialogCount、focusedWithinDialog、activeElement、beforeOpenActiveElement);results[].issues[].ruleId:ACE 规则 ID,另有message、source、path(DOM/ARIA 路径)、snippet、value。
从源码看,--levels 的四个取值在浏览器端被映射为 ACE 的判定值(evaluate_ace(),第 192-226 行):violation 对应 FAIL,potentialviolation 对应 POTENTIAL,recommendation 对应 RECOMMENDATION,manual 对应 MANUAL。因此默认 --levels violation 只报告确定违规,加宽级别会把潜在违规与改进建议纳入统计。
Markdown 报告结构(write_markdown_report(),第 533-596 行)依次为:头部元信息(生成时间、基础 URL、级别、路由、总问题数)→ Route Summary 表(Route/State、Issues、API、Failures、Dialogs、Focus In Dialog)→ Top Rules 规则计数表 → Findings By Route 明细(每条 issue 带 message、DOM path、source、snippet)。HTML 报告(write_html_report(),第 617-956 行)是自包含单文件,按“路由 → 状态 → 问题”三级折叠面板组织,支持深浅色主题,且每个路由聚合了该路由所有状态的 issue/API/失败数。
与 Playwright CI 层的关系
清单并非只服务于 Python 脚本。static-routes.a11y.spec.ts 读取同一份 scripts/a11y/a11y_routes.json(第 28-46 行),遍历 static 分组并对每条路由执行 ready 检查(支持 testId、role+name、oneOf 组合与 containsText 断言),在禁用动画、等待 networkidle 后进行 a11y 扫描;路由 id 会作为 IBM 报告标签(例如 route-settings-api-keys)。build-a11y-html-report.mjs 也读取清单,把报告标签映射回路由路径与 surface 名称。
这带来一个实操结论:新增/调整扫描路由时只改 a11y_routes.json,并配好稳定的 ready 检查,CI 与本地 Python 扫描会同步生效;反之在脚本里用 --route 临时指定的路由不会进入 CI 回归。
使用规则与注意事项
技能文档的 Rules 部分规定了使用纪律,这些规则均能在源码中找到落点:
- 只依据扫描器输出下结论:findings 全部来自 ACE 检查结果,不做人工臆测;
- 不虚构路由名:路由来源以清单和前端路由定义(
routes.tsx)为准,不要凭记忆拼路径; - 不自动乱点按钮找模态框:模态框状态必须用显式 state 动作描述(对应
run_actions的显式序列执行); - 避免破坏性模态框操作:除非用户明确要求且数据安全,否则不要执行删除类操作;
- API 请求为零时要提示:若某路由
apiRequests为空,说明扫描时未观察到任何同源 API/config/health 请求,扫描质量可能受限。扫描器本身在控制台对此发出警告(main() 第 996-997 行:warn: no same-origin API/config/health requests observed)。从 is_api_request()(第 103-107 行) 可见,判定 API 请求的标准是同源于路径包含/api/、/health或/config; - 只做报告:本技能不编辑文件;如需应用修复,应交接给
ibm-a11y-pr-remediation或ibm-a11y-level1-audit。
小结
ibm-a11y-route-scan 技能把 Langflow 前端可访问性分诊收敛为一条可复制的命令链:以 a11y_routes.json 清单的 static 分组为默认批次,用 a11y_scan.py 在 Playwright 驱动的 Chromium 中注入 IBM ACE 引擎,配合网络静默等待与 API 请求追踪,产出带 ruleId/DOM 路径/源码片段的 JSON、Markdown 或 HTML 报告;需要覆盖模态框时通过显式 state 文件描述 open/close 动作序列,动态路由则需先取到真实 ID 替换占位符。该工具定位为“报告与分诊”,与 CI 侧的 Playwright a11y 回归(src/frontend/tests/a11y/)共享同一份路由清单,两者互补而不重复。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00