关于 devtu-docs-quality
面向 ToolUniverse 文档维护者的质量审查技能,指导检查技术正确性并精简重复内容。它提供分阶段的静态检查、工具调用验证和文档审计清单。
主要功能
- 01优先核对技术正确性
- 02删除重复和过时内容
- 03扫描无效方法调用
- 04验证技能中的工具调用
- 05检查链接、篇幅与导航
适用场景
- 01审查 ToolUniverse 文档变更
- 02排查技能示例中的无效工具调用
- 03清理重复或过时的设置说明
- 04发布前执行文档质量检查
MCP 相关
输入未说明该技能与 MCP 的关系。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mims-harvard/ tooluniverse --skill devtu-docs-quality安装后在 Claude Code 中输入/devtu-docs-quality确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
API_REFERENCE.mdDOCS_STRUCTURE.mdEXAMPLES.mdSUMMARY.md详细介绍
文档质量保证
两项同等重要的优先事项
执行审核前,必须同时满足以下两项。两者互不取代。
1. 技术正确性——错误的代码比没有代码更糟;它会直接破坏用户的工作流。
2. 越少越好——冗余内容比没有内容更糟;它会造成困惑、削弱信任,并增加维护难度。
不确定技术准确性时:对照源代码验证(
src/tooluniverse/execute_function.py)。 不确定内容是否必要时:删除它。
越少越好——核心理念
ToolUniverse 是一款严肃的科研工具。文档应以克制、精准而非篇幅来体现这一点。
“越少越好”在实践中的含义
删除,而非添加。 每次编辑都应以更少的文字、更少的页面、更少的链接收尾。领域发展迅速,过时的说明会造成切实危害。若两种方法都可行,就保留一种。
每个概念只出现一次。 如果设置说明出现在三个地方,读者看其中一处时不知道还有其他内容,维护者每次修改时还必须同步更新三处。选定一个规范位置,其他地方都添加交叉链接。
不要使用表情符号。 标题、导航项、卡片标题和项目符号中的表情符号会让内容显得“由 AI 生成”,损害用户对严肃科研工具的信任。无条件删除——“强调”也不例外。
精简,而非总结。 将 1000 行页面缩减为 30 行指引链接,胜过在 1000 行页面上添加 TL;DR 摘要框。直接精简。
篇幅预算(硬性限制):
| 页面类型 | 最多行数 |
|---|---|
各平台设置页面(claude_desktop.rst、cursor.rst 等) | 15 |
| 首页网格区块 | 每个网格最多 4 张卡片 |
| “另请参阅”/“相关内容”链接列表 | 最多 4 个链接 |
| 与 Python 指南重复的教程页面 | 0——删除或改为指引链接 |
“越少越好”决策树
审核任何内容时,请问:
- 网站其他地方是否已有此内容? → 删除;添加指向规范位置的交叉链接。
- 此内容是否过时或难以保持最新? → 删除;链接到上游来源(例如
aiscientist.tools/setup.md)。 - 用户能否从首页点击 2 次以内找到此信息? → 如果可以,且信息已在相应位置,就删除此处的副本。
- 这是表情符号、装饰性标题还是填充句? → 无条件删除。
- 这是一个指向侧边栏中已有位置的“探索更多”卡片吗? → 删除。
Apple 风格简化规则
审核期间,对所有修改的文件应用这些规则。
标题、项目符号项、卡片标题、toctree 标题、导航栏条目或按钮文本中不得使用表情符号。
单一设置入口。 规范安装路径为:
Read https://aiscientist.tools/setup.md and set up ToolUniverse for me.
各平台页面必须且只能包含三项:(1)官方安装链接;(2)官方 MCP 设置指南链接;(3)上方的设置提示词。除此之外不得包含任何内容——不允许出现 JSON 片段、分步说明或故障排除内容。这些内容应放在设置技能中,位置为 aiscientist.tools/setup.md。
工具数量保持一致。 始终写作“1000+ 个工具”。不得使用“600+”“750+”“1200+”“10000+”或其他数字。
清理导航栏。 不得包含“API”“API Keys”或“Contribution”项。必须将 https://aiscientist.tools 作为第一项链接。
首页“探索更多”网格。 最多 4 张卡片。删除所有目标位置已可从侧边栏或“开始使用”区块访问的卡片。
不得存在失效的 :doc: 引用。 每个 :doc:some/path`` 都必须能对应到磁盘上的 .rst 或 .md 文件。绝不允许存在失效链接——立即删除或替换。
五阶段策略
按顺序运行各阶段——先运行 D(即时),然后运行 E(简化扫描),再运行 C/A/B。
| 阶段 | 检查内容 | 耗时 |
|---|---|---|
| D 静态方法扫描 | 错误的方法名(tu.run_batch、tu.call_tool 等) | ~2 s |
| E 简化扫描 | 表情符号、错误的数量、失效链接、大小违规 | ~10 s |
| C 实时代码执行 | 运行时故障(错误的键、错误的返回类型) | 3-5 min |
| A 自动验证 | 已弃用的命令、术语不一致 | 15 min |
| B ToolUniverse 审核 | 导航循环、重复的 MCP 配置、工具数量 | 20 min |
阶段 D:静态方法扫描
python3 - <<'EOF'
import re, sys
from pathlib import Path
from collections import defaultdict
DOCS = Path("docs")
EXCLUDE = {"locale", "old", "_build", "__pycache__", "tools", "archive"}
KNOWN_BAD = {
"list_tools", "run_batch", "run_async", "execute_tool", "call_tool",
"list_tools_by_category", "configure_api_keys", "get_tool", "get_exposed_name",
"list_available_methods", "register_tool_from_config", "register_tool",
}
STATIC = [
(r"\.load_tools\([^)]*(?:use_cache|cache_dir)\s*=", "load_tools() invalid kwargs"),
(r"ToolUniverse\([^)]*timeout\s*=", "ToolUniverse(timeout=) invalid"),
(r'"name":\s*"opentarget_', 'old lowercase "opentarget_*" tool name'),
(r"tu\.run_batch\(", "tu.run_batch() — use tu.run(list, max_workers=N)"),
(r'\btu\.[A-Z]\w+\s*\(', "tu.ToolName() shorthand — use tu.run({name:...})"),
]
M = re.compile(r'\b(?:tu|tooluni)\.([\w]+)\s*\(')
issues = defaultdict(list)
for f in sorted(list(DOCS.rglob("*.rst")) + list(DOCS.rglob("*.md"))):
if any(p in f.parts for p in EXCLUDE): continue
t = f.read_text(errors="replace")
code = "\n".join(
re.findall(r"\.\. code-block:: python\n((?:[ \t]+[^\n]*\n|[ \t]*\n)*)", t, re.MULTILINE) +
re.findall(r"```python\n(.*?)```", t, re.DOTALL))
if not code.strip(): continue
rel = str(f.relative_to(DOCS))
for m in M.finditer(code):
if not m.group(1).startswith("_") and m.group(1) in KNOWN_BAD:
issues[rel].append(f"tu.{m.group(1)}()")
for pat, label in STATIC:
if re.search(pat, code): issues[rel].append(label)
if issues:
[print(f" {f}: {i}") for f in sorted(issues) for i in sorted(set(issues[f]))]
sys.exit(1)
else:
print("Phase D clean")
EOF
修复或删除规则——没有例外:
- 存在正确的替代方案 → 立即修复
- 功能是自动的/内部的 → 删除调用,必要时添加文字注释
- 功能不存在 → 彻底删除代码块或章节
最常见的修复:
# wrong → correct
tu.run_batch(list) → tu.run(list, max_workers=4)
tu.run_async(query) → await tu.run(query)
tu.call_tool('X', {...}) → tu.run({"name": "X", "arguments": {...}})
tu.execute_tool('X', {...}) → tu.run({"name": "X", "arguments": {...}})
tu.list_tools() → tu.list_built_in_tools(mode='list_name')
tu.get_tool('X') → tu.get_tool_by_name('X')
tu.register_tool(instance) → tu.register_custom_tool(tool_instance=instance)
tu.register_tool_from_config(c) → tu.register_custom_tool(tool_config=c)
tu.configure_api_keys({}) → REMOVE — use env vars
tu.get_exposed_name(name) → REMOVE — shortening is automatic
ToolUniverse(timeout=30) → ToolUniverse() # no timeout kwarg
load_tools(use_cache=True) → load_tools() # no use_cache kwarg
tu.ToolName(key=val) → tu.run({"name": "ToolName", "arguments": {"key": val}})
opentarget_get_* → OpenTargets_get_* (capital O and T)
特殊情况: 当展示有意写错的代码作为错误示例时,将 .. code-block:: python 改为 .. code-block:: text。
阶段 E:简化扫描
python3 - <<'EOF'
import re, sys
from pathlib import Path
DOCS = Path("docs")
EXCLUDE = {"_build", "__pycache__", "locale"}
issues = []
EMOJI_RE = re.compile(
r'[\U0001F300-\U0001F9FF\U00002600-\U000027BF\U0001FA00-\U0001FA9F'
r'\U0001F004\U0001F0CF\U00002702-\U000027B0]'
)
def in_code_block(text, pos):
"""Return True if pos is inside a code block."""
before = text[:pos]
# RST code blocks
rst_starts = [m.end() for m in re.finditer(r'\.\. code-block::[^\n]*\n', before)]
for start in reversed(rst_starts):
block = text[start:]
end_match = re.search(r'\n(?![ \t]|\n)', block)
block_end = start + (end_match.start() if end_match else len(block))
if start <= pos <= block_end:
return True
# Markdown fenced blocks
fences = list(re.finditer(r'```', before))
return len(fences) % 2 == 1
for f in sorted(DOCS.rglob("*.rst")) + sorted(DOCS.rglob("*.md")):
if any(p in f.parts for p in EXCLUDE):
continue
text = f.read_text(errors="replace")
rel = str(f.relative_to(DOCS))
# Check emojis in prose
for m in EMOJI_RE.finditer(text):
if not in_code_block(text, m.start()):
line = text[:m.start()].count('\n') + 1
issues.append(f"EMOJI {rel}:{line} {repr(m.group())}")
# Check wrong tool counts
for bad in ["600+", "750+", "1200+", "10000+"]:
if bad in text and "tools" in text[text.find(bad):text.find(bad)+20]:
issues.append(f"COUNT {rel} contains '{bad} tools' (use '1000+')")
# Check broken :doc: references
for m in re.finditer(r':doc:`([^`]+)`', text):
ref = m.group(1).lstrip('/')
# Strip any display text (format: display <path>)
if ' <' in ref:
ref = re.search(r'<([^>]+)>', ref).group(1)
base = DOCS / Path(ref.replace('/', '/'))
candidates = [base.with_suffix('.rst'), base.with_suffix('.md'),
base / 'index.rst', base / 'index.md']
if not any(c.exists() for c in candidates):
line = text[:m.start()].count('\n') + 1
issues.append(f"LINK {rel}:{line} broken :doc:`{ref}`")
# Check navbar in conf.py
conf = Path("docs/conf.py").read_text()
for bad_nav in ['"API"', '"API Keys"', '"Contribution"']:
if bad_nav in conf:
issues.append(f"NAV conf.py navbar contains {bad_nav}")
if "aiscientist.tools" not in conf:
issues.append("NAV conf.py navbar missing aiscientist.tools link")
# Check per-platform page line counts
platform_dir = DOCS / "guide" / "building_ai_scientists"
for rst in platform_dir.glob("*.rst"):
if rst.name in ("index.rst", "mcp_support.rst", "compact_mode.rst",
"mcpb_introduction.rst", "mcp_name_shortening.rst"):
continue
lines = rst.read_text().splitlines()
if len(lines) > 15:
issues.append(f"SIZE guide/building_ai_scientists/{rst.name} {len(lines)} lines (max 15)")
if issues:
for i in sorted(issues):
print(i)
sys.exit(1)
else:
print("Phase E clean")
EOF
阶段 E 报告问题时,按照上文的“少即是多”决策树处理。继续阶段 C 之前,先修复所有问题。
阶段 C:工具调用检查
python scripts/check_skill_tool_calls.py
从 src/tooluniverse/data/*.json 读取工具架构,并检查技能 Markdown 中每个 tools.X(...)、tu run X '{...}' 和 {"name": "X", "arguments": {...}} 调用:工具未声明的参数(ToolUniverse 会拒绝这些参数,或者在它们与有效参数混用时静默丢弃)、缺少的必需参数,以及不存在的工具名。此检查是静态的:不会实例化任何对象,而且必须始终如此,因为某些工具类在构造时会加载数 GB 的数据集(对大量工具进行实时运行时,数据量曾超过 100 GB)。在有意展示错误用法的示例中,在调用所在行或其正上方标注 WRONG、❌ 或 # noqa: skill-call。只有在即将记录返回结构时,才逐个实时运行工具进行确认。
常见故障:
| 错误 | 原因 | 修复方法 |
|---|---|---|
KeyError: 'parameters' | tool_specification() 未使用 format="openai" | 添加 format="openai" |
OpenTargets 结果出现 KeyError: slice(...) | 对字典进行切片 | 使用 result['data']['disease']['associatedTargets']['rows'][:N] |
TypeError: load_tools() got unexpected kwarg | use_cache 或 cache_dir | 移除该关键字参数 |
阶段 A:自动验证
python scripts/validate_documentation.py
# scripts/validate_documentation.py checks:
DEPRECATED_PATTERNS = [
(r"python -m tooluniverse\.server", "tooluniverse-server"),
(r"600\+?\s+tools", "1000+ tools"),
(r"750\+?\s+tools", "1000+ tools"),
]
该检查会扫描 docs/、README.md 和 skills/(跳过 docs/dev_docs/ 和本技能,因为其中的示例会有意引用已弃用文本),打印每处匹配的 file:line,若存在任何匹配项则以状态码 1 退出。在同一行添加 noqa: docs-validate,以标记有意提及的内容。
阶段 B:ToolUniverse 专项审核
循环导航——手动追踪 index.rst → quickstart → getting_started;不允许出现循环。
工具数量——所有位置统一使用“1000+ tools”。运行:rg "[0-9]+\+?\s+(tools|integrations)" docs/ --no-filename | sort -u
自动生成的标题——docs/tools/*_tools.rst 和 docs/api/*.rst 必须以 .. AUTO-GENERATED 开头。
CLI 文档——pyproject.toml 中 [project.scripts] 下的每个条目都必须出现在 docs/reference/cli_tools.rst 中。
环境变量——src/ 中的每个 os.getenv("TOOLUNIVERSE_*") 都必须出现在 docs/reference/environment_variables.rst 中。
RST 代码块提取器(重要)
对于 RST 代码块,始终使用 re.MULTILINE(不要使用 re.DOTALL),以免合并相邻的代码块:
# correct
re.findall(r"\.\. code-block:: python\n((?:[ \t]+[^\n]*\n|[ \t]*\n)*)", text, re.MULTILINE)
# wrong — merges adjacent blocks
re.findall(r"\.\..*?code-block.*?python\n((?:[ \t]+.*\n|\n)*)", text, re.DOTALL)
验证清单
审计完成前,所有项目都必须通过。部分通过不可接受。
技术正确性:
- Phase D 扫描以 0 状态码退出——没有无效的方法调用
-
python scripts/check_skill_tool_calls.py以 0 状态码退出——技能代码块中没有未知工具或参数 - 没有未设置
format="openai"的spec['parameters'] - 自动验证通过(HIGH 级问题为 0)
- 已记录
pyproject.toml中的所有 CLI - 没有循环导航
少即是多(Apple 风格):
- Phase E 扫描以 0 状态码退出——没有表情符号、错误的数量、失效链接或超大页面
- 全站统一使用“1000+ tools”(不得使用 600+、750+、10000+ 等其他表述)
- 每个平台的设置页面仅包含安装链接、MCP 指南链接和设置提示词(≤15 行)
- 网站各处没有重复的设置说明
- 导航栏:没有“API”“API Keys”或“Contribution”;包含
aiscientist.tools链接 - 首页“Explore More”网格:卡片 ≤4 个
- 同一概念或章节不会在多个位置逐字重复出现
- “See Also”/“Related”章节的链接不超过 4 个
如果任何项目未通过:停止,修复问题,重新运行相关阶段,并确认通过。当前项目未通过前,不要继续处理下一项。
参考文件
- API_REFERENCE.md — 有效的方法签名、错误方法表和正确模式
- DOCS_STRUCTURE.md — 所有文档文件的逐文件审计状态
scripts/check_skill_tool_calls.py— Phase C 静态工具调用检查器