执行摘要
- 一句话:修复过期文档并新增文档一致性测试
- 推荐动作:值得精读测试文件
tests/test_docs_consistency.py 的设计思路,它用不到 100 行代码自动守护了全仓库文档的链接和命令有效性,可复用到其他仓库;docs/conf.py 的 _sync_examples 变更也值得关注,它展示了如何让示例文档引用真实文件而非仅有 README。若团队有类似文档漂移痛点,可将此 PR 作为模板参考。
功能与动机
PR body 为空,但测试文件 docstring 明确表达目标:Guard documentation references that can be checked without a GPU runtime. 从变更内容看,文档已与当前实现脱节:示例页链接指向带编号的旧锚点、--prompt-data 的描述未提及新增的 Parquet 格式、版本号停留在 0.0.1,而实际发布版本已迭代到 0.3.1。此 PR 旨在修正这些漂移,并建立自动化防线防止再次过期。
实现拆解
- 新增文档一致性测试(
tests/test_docs_consistency.py):新增 90 行测试文件,包含三个核心测试——test_local_markdown_links_exist 校验所有本地 Markdown 链接指向存在的文件;test_documented_script_and_test_commands_exist 校验文档中出现的脚本/测试命令路径真实存在;test_customization_anchor_links_exist 校验 customization.md 中的锚点与标题匹配。测试通过 NUM_GPUS = 0 标记为无需 GPU 的只读检查。
- 修正文档构建配置(
docs/conf.py):将 sys.path 注入方式从 os.path.abspath("../..") 改为基于 __file__ 的绝对路径,提升可移植性;版本号从 0.0.1 更新到 0.3.1;_sync_examples 中由 mkdir 改为 shutil.copytree 递归复制整个示例目录(忽略两侧 README 文件),使 _examples_synced 目录能承载示例中的辅助文件,之后再覆盖语言特定的 README。
- 更新参数帮助文本(
slime/utils/arguments.py):将 --prompt-data 的帮助信息从「仅支持 jsonl」改为「支持 JSONL 和 Parquet(Parquet 需 pyarrow)」,与实现保持同步。
- 修复 customization 文档锚点(
docs/en/get_started/customization.md、docs/zh/get_started/customization.md):将表格中的编号式锚点(如 #1-rollout-function---rollout-function-path)改为与标题自动生成的 slug 锚点(如 #rollout-function-path),避免锚点失效。
- 接入 CI(
.github/workflows/pr-test.yml、.github/workflows/pr-test.yml.j2):在零 GPU 测试矩阵中新增 test_docs_consistency.py 条目,使文档一致性检查进入主流水线。
关键文件:
tests/test_docs_consistency.py(模块 文档校验;类别 test;类型 test-coverage;符号 _markdown_files, _local_link_target, test_local_markdown_links_exist, test_documented_script_and_test_commands_exist): 新增的文档一致性测试,是本 PR 的核心防回归机制,覆盖链接、命令路径和锚点三类检查,并以零 GPU 方式接入 CI。
docs/conf.py(模块 文档构建;类别 source;类型 core-logic;符号 _sync_examples): 更新文档构建配置:版本号同步到 0.3.1、路径处理更稳健,并将示例同步从仅复制 README 改为递归复制整个示例目录,影响文档站点结构。
slime/utils/arguments.py(模块 参数解析;类别 source;类型 core-logic;符号 add_data_arguments): 更新 --prompt-data 参数的帮助文本,补充 Parquet 格式支持说明,使参数文档与实际能力一致。
.github/workflows/pr-test.yml(模块 CI 流程;类别 infra;类型 infrastructure): 在零 GPU 测试矩阵中新增 test_docs_consistency.py,使文档一致性检查进入主 CI 流程,防止回归。
docs/en/get_started/customization.md(模块 定制文档;类别 docs;类型 documentation): 修正英文版 customization 文档中的锚点,从带编号的旧锚点改为自动生成的 slug 锚点,并调整表格结构与内容。
docs/zh/get_started/customization.md(模块 定制文档;类别 docs;类型 documentation): 与英文版同步修正中文 customization 文档锚点,保持两种语言一致性。
关键符号:_markdown_files, _local_link_target, test_local_markdown_links_exist, test_documented_script_and_test_commands_exist, test_customization_anchor_links_exist, _sync_examples
关键源码片段
tests/test_docs_consistency.py
新增的文档一致性测试,是本 PR 的核心防回归机制,覆盖链接、命令路径和锚点三类检查,并以零 GPU 方式接入 CI。
"""文档一致性校验测试:无需 GPU 即可运行的只读检查。"""
import re
from pathlib import Path
from urllib.parse import unquote
ROOT = Path(__file__).resolve().parents[1]
# 需要检查的文档来源:根目录 README、中英文 docs、examples 和 docker
DOC_SOURCES = (
ROOT / "README.md",
ROOT / "README_zh.md",
ROOT / "docs" / "en",
ROOT / "docs" / "zh",
ROOT / "examples",
ROOT / "docker",
)
# 识别 Markdown 链接 : 
MARKDOWN_LINK_RE = re.compile(r"!?\[[^\]]*\]\(([^)\n]+)\)")
# 识别文档中出现的 bash/python 命令路径
COMMAND_PATH_RE = re.compile(
r"\b(?:bash|python)\s+((?:scripts?|tests)/[A-Za-z0-9_.+/-]+\.(?:sh|py))"
)
def _markdown_files():
"""按顺序产出所有需要检查的 Markdown 文件,跳过示例生成的同步目录。"""
for source in DOC_SOURCES:
if source.is_file():
yield source
else:
# 对目录递归查找 *.md,同时排除 docs 构建时自动生成的 _examples_synced
for path in sorted(source.rglob("*.md")):
if "_examples_synced" not in path.parts:
yield path
def _local_link_target(raw_target: str) -> str | None:
"""从原始链接文本中提取本地文件路径。
过滤掉锚点链接、绝对路径、URL、邮箱和 data URI;
返回解码后的相对路径,便于与文件系统比对。
"""
target = raw_target.strip()
if target.startswith("<") and ">" in target:
target = target[1:target.index(">")]
else:
target = target.split(maxsplit=1)[0] # 去掉标题和空白
if target.startswith(("#", "/")) or "://" in target or target.startswith(("mailto:", "data:")):
return None
# 去掉 #anchor 和 ?query 后做 percent-decode
return unquote(target.split("#", 1)[0].split("?", 1)[0]) or None
def test_local_markdown_links_exist():
"""校验文档中的所有本地相对链接都指向存在的文件。"""
missing = []
for markdown_file in _markdown_files():
text = markdown_file.read_text(encoding="utf-8")
for raw_target in MARKDOWN_LINK_RE.findall(text):
target = _local_link_target(raw_target)
if (
target is not None
and "_examples_synced" not in Path(target).parts
and not (markdown_file.parent / target).exists()
):
missing.append(f"{markdown_file.relative_to(ROOT)} -> {target}")
assert not missing, "Broken local Markdown links:\n" + "\n".join(missing)
def test_documented_script_and_test_commands_exist():
"""校验文档中出现的脚本/测试命令路径真实存在。"""
missing = []
for markdown_file in _markdown_files():
text = markdown_file.read_text(encoding="utf-8")
for command_path in COMMAND_PATH_RE.findall(text):
if not (ROOT / command_path).is_file():
missing.append(f"{markdown_file.relative_to(ROOT)} -> {command_path}")
assert not missing, "Documented command paths do not exist:\n" + "\n".join(missing)
docs/conf.py
更新文档构建配置:版本号同步到 0.3.1、路径处理更稳健,并将示例同步从仅复制 README 改为递归复制整个示例目录,影响文档站点结构。
def _sync_examples(app):
"""在 Sphinx 构建前,将 examples 下的示例同步到各语言文档目录。
每个子目录优先使用语言对应的阅读文件(zh 用 README_zh.md),
找不到时回退到 README.md;同时递归复制整个示例目录,
以便文档可以引用示例中的其他辅助文件。
"""
if not src_dir.is_dir():
return
for lang, cfg in lang_cfgs.items():
lang_dir = cfg["dir"]
if not lang_dir.exists():
continue
out_dir = lang_dir / "_examples_synced"
# 每次构建前先清除旧目录,避免残留文件
if out_dir.exists():
shutil.rmtree(out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
for d in sorted(src_dir.iterdir()):
if not d.is_dir():
continue
if lang == "zh":
# 中文文档优先使用 README_zh.md,缺失时回退 README.md
primary = d / "README_zh.md"
fallback = d / "README.md"
candidate = primary if primary.exists() else fallback
else:
candidate = d / "README.md"
if not candidate.exists():
continue
target_dir = out_dir / d.name
# 递归复制示例目录,排除两侧的 README,随后统一覆盖为目标阅读文件
shutil.copytree(d, target_dir, ignore=shutil.ignore_patterns("README.md", "README_zh.md"))
shutil.copy2(candidate, target_dir / "README.md")
entries.append((d.name, f"_examples_synced/{d.name}/README.md"))
评论区精华
该 PR 没有任何 review 评论或 thread,作者直接合入。从最终状态可推断改动被维护者接受,无需额外讨论。
风险与影响
- 风险:
- 文档构建逻辑变更:
docs/conf.py 中 shutil.copytree 会递归复制整个示例目录,若示例中包含大文件或非常规文件(如隐藏文件、符号链接),可能拖慢文档构建甚至触发构建错误;同时 _examples_synced 目录内容增多,可能引入意外文件暴露到文档站点。
- 新增 CI 测试的误报风险:
test_local_markdown_links_exist 使用正则解析 Markdown 链接,遇到包含空格或特殊字符的合法链接可能误判为损坏;test_documented_script_and_test_commands_exist 严格校验命令行路径,文档中如果演示「不存在的路径」作为示例会直接导致 CI 失败。
- 参数帮助文本与实现的潜在不一致:
slime/utils/arguments.py 仅更新了帮助文本,若 Parquet 支持尚未合并或存在平台差异,文档会形成新的误导。
- 锚点变更的对外兼容性:
customization.md 的锚点从带编号改为 slug 形式,若外部链接或书签引用了旧锚点,将变成死链。
- 影响:用户影响:文档更贴近当前实现,减少配置时的困惑,尤其是 --prompt-data 的 Parquet 说明和示例命令的修正;系统影响:CI 新增零 GPU 快速测试,对整个流水线耗时影响极小;团队影响:文档维护成本降低,死链和过期命令能在合入前被发现,长期提升文档可信度。
- 风险标记:文档构建逻辑变更, 新增 CI 测试, 链接规则可能产生误报
关联脉络
- PR #2326 [doc] update doc: 同样属于文档维护类 PR,更新 README 与 CONTRIBUTING,与本 PR 共同组成近期文档改进线。
- PR #2297 fix: reject non-positive rollout temperature at parse time: 修改了 slime/utils/arguments.py,与当前 PR 在同一文件有交集,便于追踪参数相关变更的演化。
参与讨论