- 人工智能
- AI 应用
- AI 技能
- RAG
- MCP 服务
- 网页爬虫
【免费下载链接】Skill_Seekers
Convert documentation websites, GitHub repositories, and PDFs into Claude AI skills with automatic conflict detection
导读
本文以 Skill_Seekers 仓库中 Jupyter Notebook 转 Skill 流水线的 golden 输出示例tests/golden/phase2/jupyter_kw/references/section_s3-s4.md为切入点,逐行拆解 Skill_Seekers 如何把一个.ipynb中无标题、异常报错、未渲染的"杂项"单元转换为结构化 Markdown 参考文件。读者将掌握参考文件的命名规则、代码单元与 Raw 单元的具体渲染格式、关键词分类与 "Other" 兜底机制,以及 golden 测试如何保证输出字节级稳定。
一、关联文档在产物树中的定位
tests/golden/phase2/jupyter_kw/是 Skill_Seekers 为 Jupyter 抓取器(src/skill_seekers/cli/jupyter_scraper.py)在 Phase 2 重构(迁移到DocumentSkillBuilder)前后输出一致性而提交的 golden 基线树。该目录包含:
SKILL.md:生成的主技能入口,含 Section Overview、Dependencies、Code Examples、Notebook Statistics 等汇总;references/index.md:整个 notebook 的分类索引,列出 5 个分类、各类包含的 section 区间、统计信息与导入包列表;references/section_*.md:每个分类对应一个参考文件,section_s3-s4.md正是其中之一。
关联文档section_s3-s4.md属于golden_jupyter_kw构建产物,对应测试 test_jupyter_keyword_categorization_matches_golden 中"Other"分类的输出。它只包含两个单元(Section 3 与 Section 4),恰好覆盖了流水线中最容易被忽略的两种单元类型——抛出异常的代码单元与Raw 原始单元,因此是理解整个渲染管线最浓缩的标本。
从 references/index.md 的 Categories 一节可以看到它的归属:
- [Other](https://link.gitcode.com/i/b7f9c2e704a14162f9f90afaeedb8336) (2 sections, Sections 3-4)而index.md的 Statistics 提供了全局口径:5 个 section、2 个 code 单元、2 个 markdown 单元、1 个 raw 单元、1 个 notebook,导入包为numpy、pandas、sklearn三个。这些数字与 test_phase2_golden_jupyter.py 中的SECTIONS与_extracted_data完全对应。
二、参考文件命名规则:section_s3-s4.md从何而来
参考文件的名字不是随意的,其生成逻辑集中在 src/skill_seekers/cli/scraper_utils.py 的reference_filename()函数中,它是文件写入器、index.md和SKILL.md导航三处共用的"唯一事实来源",以避免链接漂移(代码注释中标记为 DOC-07)。
命名分三种情况:
- 分类为空:回退为
section_<序号>.md(两位数字补齐),例如空分类的 references/section_04.md; - 只有一个分类:使用源文件 stem 命名(如
analysis.md),无 stem 时用main.md; - 多分类(当前场景):取分类内最小与最大 section 号拼成区间,格式为
<base>_<prefix><min>-<prefix><max>.md。
golden_jupyter_kw走第三条路径:其分类来源是测试传入的categories字典(见 test_phase2_golden_jupyter.py),含setup、modeling、loading、empty_cat四个显式分类。分类完成后,"Other" 桶收集了未被任何分类命中的 section 3(Magic 单元)与 section 4(Raw 单元),section 号区间为 3–4,因此得到文件名section_s3-s4.md——s是prefix参数的默认值,base_stem为空时取"section"。同一逻辑也解释了同目录下section_s1-s1.md、section_s5-s5.md的命名。
三、代码单元渲染:标题推断、执行计数与错误输出
section_s3-s4.md的第一段完整展示了无执行计数的代码单元是如何被渲染的:
**📄 Source: Section 3** (Code Cell) #### Magic: %timeit ```python %timeit broken()Errors:
NameError: name 'broken' is not defined Traceback line 1Tags: raises-exception
逐行对应 [jupyter_scraper.py](https://link.gitcode.com/i/9735c98d6b9f8bb35b679f4aca3d9901) 中的处理: ### 3.1 单元头部与执行计数 渲染入口是 `_write_reference_section()`([jupyter_scraper.py](https://link.gitcode.com/i/9735c98d6b9f8bb35b679f4aca3d9901#L749-L794)):代码单元固定输出 `**📄 Source: Section N** (Code Cell)`,若存在执行计数会追加 `[In N]` 标记(如 `(Code Cell [In 2])`)。本单元 `execution_count` 为 `None`,因此不显示 `[In N]`——这正是 [test_phase2_golden_jupyter.py](https://link.gitcode.com/i/2cbf673e4405cd4929710920525fcf79) 中 section 3 的数据形态。 ### 3.2 Magic 命令的标题推断 代码单元本身没有 Markdown 标题,其标题由 `_infer_code_heading()`([jupyter_scraper.py](https://link.gitcode.com/i/9735c98d6b9f8bb35b679f4aca3d9901#L583-L605))按优先级推断: - 首行是 `# 注释` → 截取注释文本(超过 80 字符截断); - 首行是 `def`/`class`/`async def` → `Define: <名字>`; - 首行是赋值表达式 → `Assign: <变量名>`; - 首行是 **magic 命令(`%` 开头)** → `Magic: <命令>`; - 首行以 `!` 开头 → `Shell: <命令>`; - 兜底 → `Code Cell [<执行计数>]`。 `%timeit broken()` 命中第四支,产出 `Magic: %timeit`。写入时标题层级在 `heading_level`(h3)基础上加一级渲染为 `####`,因此参考文件中出现 `#### Magic: %timeit`。 ### 3.3 错误输出的收集与格式化 异常信息来自 `_parse_code_cell()`([jupyter_scraper.py](https://link.gitcode.com/i/9735c98d6b9f8bb35b679f4aca3d9901#L497-L562))对 `outputs` 中 `output_type == "error"` 的处理:拼接 `ename: evalue`(`NameError: name 'broken' is not defined`)后追加清理过的 traceback 行(剥离 ANSI 颜色码 `\x1b[...m`),得到第二行 `Traceback line 1`。写入时统一放进 `**Errors:**` 代码块([jupyter_scraper.py](https://link.gitcode.com/i/9735c98d6b9f8bb35b679f4aca3d9901#L783-L784))。与之相对,`stream` 输出进 `**Output:**` 块,`execute_result`/`display_data` 的 `text/plain` 进正文文本,`text/html`、`image/png`、`image/svg+xml` 等富输出则折叠为一行 `*Rich output: <mime 列表>*`。 ### 3.4 单元标签 nbformat 中 cell 级 `metadata.tags` 会被保留并渲染为 `*Tags: ...*` 行([jupyter_scraper.py](https://link.gitcode.com/i/9735c98d6b9f8bb35b679f4aca3d9901#L791-L793))。本单元携带 `raises-exception` 标签,在 SKILL 中成为一种语义标注,便于 Agent 检索"会抛异常的示例"。 ## 四、Raw 单元的极简渲染 `section_s3-s4.md` 的第二段是全文件最短的一段: ```markdown **📄 Source: Section 4** (Raw Cell) raw front-matter contentRaw 单元(通常用于存放 front-matter、LaTeX、不可渲染的源文本)由_parse_raw_cell()(jupyter_scraper.py)处理:无标题、无代码样例、无输出,仅保留source.strip()作为正文,写入时头部标记为(Raw Cell)(jupyter_scraper.py)。这段渲染刻意"裸奔",保证 front-matter 类内容不被二次加工破坏,可直接被下游解析器消费。
五、为什么这两个单元会进入 "Other" 分类
"Other" 桶的形成取决于分类策略。categorize_content()(jupyter_scraper.py)按优先级依次处理:
- 单文件来源:直接用 notebook 的 stem 作为唯一分类(本 golden 分支未启用,因为测试配置未把
notebook_path设为文件); - 显式关键词分类:对每个 section 拼接
text + heading + code(_section_text(),jupyter_scraper.py),逐个匹配categories字典中的关键词,命中数最多的分类胜出;没有任何命中则落入other; - 自动主题分类:无显式分类时使用内置
_TOPIC_KEYWORDS(jupyter_scraper.py,涵盖 data_loading、modeling、evaluation 等 8 类主题),得分 ≥2 才入桶,否则进other。
在test_jupyter_keyword_categorization_matches_golden中,测试传入:
categories={ "setup": ["install", "setup"], "modeling": ["accuracy", "recall", "model"], "loading": ["read_csv"], # 仅能命中代码样例中的 read_csv "empty_cat": ["zzz-no-match"], # 永远不会命中 → 空分类 }Section 3 的%timeit broken()与 Section 4 的raw front-matter content均不包含任何分类关键词(%timeit不在 modeling 关键词中、broken与raw亦不匹配),于是双双落入other(标题渲染为 "Other")。而 section 1 因含setup、install命中setup,section 5 因含accuracy、recall、model命中modeling,section 2 的read_csv命中loading——这正是 index.md 中五个分类数量(1/1/1/0/2)的成因。
六、golden 测试如何保证输出不变
tests/golden/phase2/jupyter_kw/不是随手生成的文档,而是回归测试的字节级基线。tests/phase2_golden_utils.py 定义了校验协议:
- 重构前用
UPDATE_GOLDENS=1 pytest <测试名>从旧代码捕获基线并提交; - 重构后测试在比对模式下运行,
assert_matches_golden()(tests/phase2_golden_utils.py)会对skill_dir下所有文件做集合与逐字节比对,任何差异(包括空白、换行)都会以 unified diff 形式抛错; - 其 docstring 明确警告:
UPDATE_GOLDENS=1会重写已提交基线,只能在刻意更新时使用。
因此section_s3-s4.md的每一行都等价于一条约束——只要JupyterToSkillConverter的渲染逻辑有任何行为变化,测试 test_phase2_golden_jupyter.py 就会立即失败。这也是将golden_jupyter_kw归类到"目录来源 + 显式关键词分类 + 空分类 + other 兜底"组合的原因:该分支覆盖了_reference_filename的区间命名、空分类回退(section_04.md)、关键词打分与兜底桶等最易回归的路径。
七、从单文件到整个技能包:参考文件的消费方式
section_s3-s4.md最终通过两条路径被 Agent 消费:
SKILL.md的Navigation一节(SKILL.md)列出全部references/*.md及其分类名,链接同样由reference_filename()统一生成,保证不会指向不存在的文件;- 顶部 front-matter 的
name与description(如Use when testing the golden_jupyter_kw golden build)供 Agent 在技能选择阶段判断何时加载;description若未显式配置,会由infer_description_from_notebook()(jupyter_scraper.py)依据language_info、kernelspec.display_name等 notebook 元数据自动生成。
用户在自己的 notebook 上复现同样的产物,只需安装nbformat后运行(单文件或目录均可,见 jupyter_scraper.py 用法注释):
pip install "skill-seekers[jupyter]" skill-seekers jupyter --notebook analysis.ipynb --name myskill skill-seekers jupyter --notebook ./notebooks/ --name myskill skill-seekers jupyter --from-json notebook_extracted.json生成的技能目录结构即为本 golden 树所展示的形态:SKILL.md+references/index.md+ 按分类区间命名的references/section_*.md。
八、小结
section_s3-s4.md虽只有寥寥数行,却是 Skill_Seekers Jupyter 转 Skill 管线的浓缩横截面:它同时验证了 Magic 标题推断、无执行计数代码单元渲染、ANSI 清理后的错误输出、cell 标签保留、Raw 单元透传、关键词分类的 "Other" 兜底以及多分类区间命名这七项机制。结合 jupyter_scraper.py 的源码与 test_phase2_golden_jupyter.py 的测试数据,可以确认:参考文件不是临时拼凑的摘要,而是由nbformat解析、DocumentSkillBuilder组装、reference_filename()统一命名、golden 测试逐字节守护的规范化产物——这正是 Skill_Seekers 把任意 notebook 变成可被 Agent 引用、检索和复现的知识技能包时的核心保障。
- 人工智能
- AI 应用
- AI 技能
- RAG
- MCP 服务
- 网页爬虫
【免费下载链接】Skill_Seekers
Convert documentation websites, GitHub repositories, and PDFs into Claude AI skills with automatic conflict detection
相关推荐
Skill_Seekers 的 Jupyter Notebook 转 Skill:Reference Index 索引文件的设计、生成与 Golden 验证
Skill_Seekers 的 Jupyter Notebook 转 Skill:Reference Index 索引文件的设计、生成与 Golden 验证 S
人工智能AI 应用AI 技能RAGMCP 服务网页爬虫Skill_Seekers PDF 分页参考文档生成机制解析:从关键词分类到 Golden 测试
Skill_Seekers PDF 分页参考文档生成机制解析:从关键词分类到 Golden 测试 本文以仓库中 golden 测试产物 tests/golden
人工智能AI 应用AI 技能RAGMCP 服务网页爬虫深入 Vanity 源码:适配器模式与 Playground 架构设计解析
深入 Vanity 源码:适配器模式与 Playground 架构设计解析 本文带你深入 Vanity 源码,拆解这个面向 Ruby/Rails 的 A/B 测
人工智能AI 应用AI 技能RAGMCP 服务网页爬虫
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考