- 后端
- 开发工具
【免费下载链接】explainshell
match command-line arguments to their help text
本指南完整讲解 Explainshell 仓库中eval-render技能(.claude/skills/eval-render/SKILL.md)的用法:在升级tools/mandoc-md前,如何对候选 mandoc 二进制做端到端渲染评估、解读结构指标差异、判定合并/回退/延期结论,并在出现回退时生成可交付给 mandoc 源码工作区 Agent 的交接提示。读完本文,你将能独立跑通"渲染基线→渲染候选→对比→分类→截图报告→裁决→交接"的完整评估闭环,并理解其背后的源码机制。
评估工作流总览
eval-render的本质是回归评审工具而非黄金快照测试(golden snapshot test)——这一点在其底层实现 render_eval.py 的模块 docstring 中有明确说明。评估不追求"输出与预期逐字节一致",而是回答三个问题:
- 一次 markdown 渲染改动,是否影响了原本渲染正常的 manpage?
- 渲染出的 HTML 结构是否出现意外变化?
- 已知的压缩选项清单(如 ImageMagick 页面)是否在结构上变得更有用?
工作流共七步:渲染基线与候选、对比、逐页分类、生成截图 diff、套用裁决标准、生成交接提示(仅回退时)、输出最终报告。候选二进制通常是打了补丁的 mandoc 源码树,例如~/dev/vibe/mandoc-1.14.6/mandoc,而基线是仓库当前捆绑的tools/mandoc-md。
调用语法(该技能可由用户直接触发,即user_invocable: true):
/eval-render <candidate-mandoc> [--mandoc-worktree <path>]| 参数 | 必填 | 说明 |
|---|---|---|
candidate-mandoc | 是 | 补丁后 mandoc 二进制的绝对路径,如~/dev/vibe/mandoc-1.14.6/mandoc |
mandoc-worktree | 否 | 构建候选二进制的 mandoc 源码工作区路径,用于交接提示;省略时从候选二进制的父目录推断 |
第 1 步:渲染基线与候选
先后执行两次渲染,全语料(corpus)每次约耗时 10–30 秒:
source .venv/bin/activate && \ python tests/evals/render/render_eval.py render --label baseline-tools-mandoc-md --mandoc tools/mandoc-md python tests/evals/render/render_eval.py render --label candidate-<short-tag> --mandoc <candidate-mandoc>选择一个能区分候选的短标签,例如paragraphize-fix、synopsis-v2。两次运行的输出末尾都会有run directory:行,记下两个运行目录的路径,供后续compare使用。
底层机制:render_run()(render_eval.py)逐页调用_mandoc_markdown()(render_eval.py),即对每个 manpage 执行mandoc -T markdown,非零退出码或空输出会抛出异常并被记为 failure;随后用 web/markdown.py 的render_markdown()渲染 HTML(内部先转义裸<word>占位符再走 cmark-gfm 的cmarkgfm.markdown_to_html,出错时回退为转义文本),并对 markdown、过滤后文本与 HTML 三份产物分别计算指标。每个运行目录下生成三类工件:
markdown/*.md:mandoc-T markdown原始输出html/*.html:经 Explainshell cmark-gfm 路径渲染的 HTMLmetrics/*.json:每页的结构指标
运行元数据(label、timestamp、mandoc 路径、git 提交、语料列表、页面数与失败数)写入summary.json。渲染结束会打印rendered pages:与failures:统计;--fail-on-failure可在有页面渲染失败时令进程以非零码退出,适合 CI 风格场景。未显式指定--mandoc时默认使用config.MANDOC_PATH(config.py 中的环境变量,缺省指向仓库内tools/mandoc-md)。
第 2 步:对比两次运行
source .venv/bin/activate && \ python tests/evals/render/render_eval.py compare <baseline-run> <candidate-run>完整阅读输出。其中"Suspicious structural changes"(可疑结构变化)一节列出了每一个有指标变动的页面;之后的"Metric deltas"一节给出每页具体指标的before -> after明细,comparison.md也会写入当前运行目录。
底层机制:compare_runs()(render_eval.py)通过_suspicious_changes()(render_eval.py)做逐页对比。该函数维护一张"指标→容差"检查表:结构性指标(如markdown.max_line_length、markdown.giant_lines_500、markdown.unescaped_star_runs、html.tags.h2、html.tags.tr等)容差全部为 0.0——只要出现非零 delta 就标为可疑;而markdown.char_count、html.data_chars这类内容量指标采用 0.02 的相对容差(abs(delta)/max(abs(before),1) > 0.02才算变化)。设计意图在注释中写得很清楚:"cheap structural metrics rarely move by accident",即便宜的结构指标很少意外变动,误报可容忍,而小的结构性改进恰恰是评估想要的信号。filtered.removed_sections的变化也单独列出。
第 3 步:为每个可疑页面分类
对每个可疑页面,在improvement(改进)、regression(回退)、ambiguous(不确定)三者中择一:
compare已经告诉了你哪些指标动了、动了多少——那就是检查本身。评估的标签集覆盖链接、代码、强调、标题、表格行、段落、列表与内容长度,任何语义上有意义的变化都会体现在 delta 中。读取每页的指标块,自问:"这些变动是否全部指向同一方向?该方向是否符合补丁声明的目的?",然后给出结论。- 当 delta 混杂、或以补丁描述无法解释的方式出人意料时,归类为ambiguous,不要猜测。
度量矩阵:每页指标分为三组(见_metrics(),render_eval.py):
markdown.*行级指标(_line_metrics(),render_eval.py):行数、非空行数、字符数、最大/平均行长、>500/1000 字符的巨型行数、类选项 token 数(正则(?<![\w\\])(?:\\?-{1,2}|\\\[mi\])[-A-Za-z0-9][-_A-Za-z0-9]*匹配)、单行多 token 计数、未转义的*与_连串数;filtered.*过滤后指标(_filtered_metrics(),render_eval.py):先经 extraction/llm/text.py 的clean_mandoc_artifacts()清洗,再由filter_sections()(text.py)剔除无关章节,统计清洗后的行数/字符数与removed_sections;html.*结构指标(_html_metrics(),render_eval.py):用标准库HTMLParser子类TagCounter统计a、blockquote、br、code、em、h1–h6、li、ol、p、pre、strong、table、tr、ul等 25 种标签的计数,以及文本字符数、最大嵌套深度和\ [ ] * _ \< >` 敏感字符直方图。
第 4 步:生成截图 diff 报告
后台运行截图对比,供用户(及必要时你自己)视觉核验。约 10 页 × 每页 ~10 秒:
source .venv/bin/activate && \ python tests/evals/render/render_eval.py diff <baseline-run> <candidate-run>建议以run_in_background: true方式执行。diff 报告路径为<candidate-run>/diff-report/index.html。
底层机制:diff_report()(render_eval.py)为每个可疑页面生成 expected/actual 两个自包含 HTML 评审页(_write_review_page(),render_eval.py),调用npx playwright screenshot --browser chromium截图(_screenshot_page(),render_eval.py),超长页面自动回退到--clip-height 12000的视口裁剪模式(_screenshot_with_clip_fallback(),render_eval.py),再用_pad_screenshots_to_match()(render_eval.py)把较短图片顶部补白使两张 PNG 同尺寸,保证比较滑杆干净裁切。最终由_write_diff_index()(render_eval.py)生成单页索引:每页一张卡片,内含 slider / side-by-side / actual / expected 四种查看模式(基于img-comparison-slider)、触发标记原因、"Metric deltas" 折叠明细,以及指向 expected/actual 的 HTML 与 markdown 工件链接。页面下拉框支持 URL hash 直达与 hashchange 同步。若浏览器缺失,先执行npx playwright install chromium。
常用选项(对应 build_parser() 的 diff 子命令):
# 对全部页面截图,而不只是可疑页面 python tests/evals/render/render_eval.py diff BASE CURRENT --all # 迭代期限制报告规模 python tests/evals/render/render_eval.py diff BASE CURRENT --limit 3 # 把报告写到其他位置 python tests/evals/render/render_eval.py diff BASE CURRENT --output /tmp/render-diff # 自定义 Playwright 截图超时(毫秒,默认 30000) python tests/evals/render/render_eval.py diff BASE CURRENT --timeout 60000第 5 步:套用裁决标准
- merge(合并)⇢ 每个可疑页面都归类为 improvement。建议用户执行
cp <candidate-mandoc> tools/mandoc-md并提交;同时建议跑一次/eval-llm作为后续的直觉检查(gut-check)。 - regression(回退)⇢ 存在任一归类为 regression 的页面。进入第 6 步生成交接提示。
- defer(延期)⇢ 存在任一 ambiguous 页面,或评审者自身不确定。询问用户如何继续。
注意cp与git commit只是建议用户执行的晋升命令,仓库本身是只读的,本文仅说明操作方式。
第 6 步:交接提示(仅回退时)
裁决为regression时,推断 mandoc 工作区路径(默认取候选二进制的父目录,除非显式给了--mandoc-worktree),然后产出一段自包含的提示,用户可将其粘贴到运行在该工作区的兄弟 Claude 会话中。提示必须包含:
- 候选二进制的确切路径及其调用方式;
- 语料运行目录(供上游 Agent 读取生成的 markdown/HTML);
- 每个回退页面:页面路径、回退指标及 before→after 数值、根据指标模式推断的一行原因假设;
- 下游渲染管线指引,便于上游 Agent 追踪其 markdown 会变成什么:Explainshell 把
mandoc -T markdown输出经 web/markdown.py(CommonMark)渲染后才展示; - 成功标准:重建 mandoc,然后从 Explainshell 仓库重跑
/eval-render <path>,确认裁决翻转为merge。
标准格式如下(生成时替换为实际值,回退页详情必须具体:真实文件路径、真实指标数字):
You're in the mandoc-1.14.6 source tree. Explainshell's render eval flagged the following regressions in your last build at <candidate-mandoc>: **Regressing pages**: - <page-path>: - <metric>: <before> -> <after> - Likely cause: <one-line hypothesis from the metric pattern> [repeat per page] **Rendering pipeline**: explainshell renders the `-T markdown` output through CommonMark (`explainshell/web/markdown.py`) before display, so anything CommonMark interprets specially (4-space indent → code block, blank lines → paragraph break, etc.) shapes the final HTML. Read the candidate markdown for the regressing pages to understand why the metric moved. **Reference artifacts** (read-only): - baseline markdown/HTML: <baseline-run>/{markdown,html}/ - candidate markdown/HTML: <candidate-run>/{markdown,html}/ **Iterate**: 1. Make a fix in the mandoc source. 2. `make` to rebuild. 3. From <explainshell-repo>, run `/eval-render <candidate-mandoc>`. 4. Stop when the verdict flips to "merge".为什么强调 CommonMark:Explainshell 的展示链路并非"mandoc 输出即所得"。render_markdown()会先转义<...>占位符,再交给 cmark-gfm 处理,因此 CommonMark 的特殊语义(4 空格缩进 → 代码块、空行 → 段落分隔等)会直接塑造最终 HTML。交接提示中的这条指引,正是让上游 Agent 去读回退页面的候选 markdown,理解指标为何移动。
第 7 步:输出最终报告
最终面向用户的报告(在聊天中给出,不写文件):
- 一行结论(merge/regression/defer)及置信度说明;
- 语料统计:总页面数、按分类拆分的可疑页面数;
- 运行目录路径与 diff 报告 URL;
- 每个可疑页面一行小结(
<page>: <classification> — <one-line reason>); - 若merge:给出晋升用的确切
cp+git commit命令; - 若regression:给出交接提示的 fenced block;
- 若defer:列出触发延期的页面/指标,以及能够化解它们的证据。
语料组织与配置要点
语料文件为 tests/evals/render/corpus.txt,每行一个仓库相对路径的 manpage,#注释与空行被忽略(解析逻辑见_read_corpus(),tests/evals/_common.py)。路径经explainshell-manpagesgit 子模块(挂载于manpages/)解析,首次使用先初始化:
git submodule update --init默认语料混合了三类页面:
- 应当已渲染良好的 staple 页(
grep、sed、ssh、tar等); - 大型选项密集页(
curl、find、ps、xz等); - 已知 markdown 当前会折叠选项清单的 ImageMagick 页。
增删页面直接编辑corpus.txt;若只想渲染临时子集而不动语料,可在render后直接传路径:
python tests/evals/render/render_eval.py render \ --label imagemagick-only \ --mandoc ~/dev/vibe/mandoc-1.14.6/mandoc \ manpages/arch/latest/1/convert.1.gz \ manpages/arch/latest/1/magick.1.gzrender子命令的完整参数(build_parser()):--label(必填,人类可读标签)、--mandoc(二进制路径,默认取配置)、--corpus(默认tests/evals/render/corpus.txt)、--output(运行输出目录)、--fail-on-failure。
查看与检查建议
- 视觉评审优先看
diff命令产出的diff-report/index.html——每个可疑页面都有滑杆前后的 expected/actual 截图,并带回到渲染 HTML 与原始 markdown 的链接; - 纯数字评审看
compare产出的comparison.md——列可疑结构变化及其背后的指标 delta。当评估"内容是否仍自然流动"时,优先审渲染后的 HTML 而非原始 markdown; - 支持 CI 式使用:
compare BASE CURRENT --fail-on-suspicious在检测到可疑结构变化时以非零码退出; - 该工具不是黄金快照测试,刻意不接入
make tests-all。应在修改tools/mandoc-md、web/markdown.py 或clean_mandoc_artifacts/filter_sections辅助函数时手动运行。
补充:无基线的绝对缺陷审计
render_eval.py还提供audit子命令(render_eval.py),对一次渲染运行做与基线无关的绝对渲染缺陷扫描,用于捕获两类信号:已知会产出quad_star_run的页面仍保留在语料中(见 corpus.txt 注释),保证audit有信号可查。内置 11 条规则(AUDIT_RULES,render_eval.py):
| rule_id | 扫描内容 |
|---|---|
quad_star_run | 4+ 星号连串(如**foo****bar**) |
empty_emphasis_tag | 空<em>/<strong>标签 |
roff_named_escape | \[name]形式泄漏到输出(对照 mandoc_char(7) 规范转义名集合KNOWN_MANDOC_ESCAPE_NAMES过滤误报) |
roff_two_letter_escape | \(xx形式泄漏到输出 |
roff_font_escape | \fX字体转义泄漏到输出 |
visible_zwnj_entity | 文本中的字面‌实体 |
visible_nbsp_entity | 文本中的字面 实体 |
visible_double_amp | 双重编码的&amp; |
visible_open_double_backtick | markdown 中字面\``(不对称排版开引号) |
giant_markdown_line | 超过 2000 字符的 markdown 行 |
synopsis_no_spaces_run | SYNOPSIS 中 >200 字符且无空白符的行 |
已知渲染怪癖
.TP \(带\占位符的空标签段落)在 HTML 中渲染为<p> </p>——一个可见的空行块。早期版本的 mandoc-md 会将其输出为****行,CommonMark 会折叠成<hr />主题分隔线;当前二进制保留了作者"空段落"的原始意图。因此在每个此类位置,候选页面都比基线垂直更高,但不会出现伪水平分隔线。这是有意设计,评估时不应误判为回退。
- 后端
- 开发工具
【免费下载链接】explainshell
match command-line arguments to their help text
相关推荐
Apache Weex Native渲染引擎性能测试:评估渲染效率
Apache Weex Native渲染引擎性能测试:评估渲染效率 Apache Weex是一个跨平台UI框架,支持Android、iOS和Web平台。本文将详
移动开发跨平台原生移动前端nerfstudio 渲染指南:使用 ns-render 输出 NeRF 视频、图像与数据集渲染结果
nerfstudio 渲染指南:使用 ns render 输出 NeRF 视频、图像与数据集渲染结果 本篇技术指南围绕 nerfstudio 的 ns rend
计算机视觉深度学习图形学Hugo 渲染钩子(Render Hooks)完全指南:用模板覆盖 Markdown 到 HTML 的渲染
Hugo 渲染钩子(Render Hooks)完全指南:用模板覆盖 Markdown 到 HTML 的渲染 导读 本文围绕 Hugo 的渲染钩子(Render
开发工具前端CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考