从电子书中提取结构化知识的完整工作流。 支持 PDF / EPUB / MOBI / TXT / MD 等多种电子书格式。 特点:(1) 分块处理避免上下文爆炸,绝不直接读取全书; (2) 每块串行通过 subagent 提取名词,结合互联网检索给出解释; (3) 最终汇总到 SQLite 数据库。 当用户需要"提取电子书知识"、"做读书笔记"、"整理书中人物/事件"、 "归档书中概念"、"结构化电子书内容"时务必使用此 skill。 也适用于用户想从书中提取具有民族特色或地方特色的文化元素时。
Resources
3Install
npx skillscat add honosumi/i-desire-more-knowledge-skill Install via the SkillsCat registry.
电子书知识提取工作流
核心原则
- 断点续传 — 每次启动必须先检测断点(扫描
{book_name}_*.json文件),有已完成块则从断点继续,无断点才全新开始。严禁不检查直接从头处理。 - 禁止直接读取整本电子书 — 对电子书的访问仅限于读取文件名、目录列表和分块后的文本块
- 分块处理 — 每个文本块约 5000 字符,块间 100 字符重叠,保证上下文连贯但不溢出
- 串行处理 — 每个文本块依次处理,禁止并行启动 subagent,确保质量
- 知识型条目优先 — 提取的是"知识点"而非"名词"。判断标准:一个不了解背景的读者是否需要查资料才能理解?如果是,才提取。主角名字、普通日常物品、通用概念一律不收录
- 纯词汇输出 — 关键词文件只允许包含纯词汇(每行一个),禁止写入类别名、分组标题、分隔线等任何非词汇内容
- 互联网辅助 — 对每个条目检索互联网,结合书中内容进行解释,不做纯虚构
- YAML 四字段约束 — 每个知识条目严格 4 字段:
名词、解释、书中原文、网络来源。禁止出现任何额外字段。 - subagent 静默模式 — subagent 只产出文件,禁止在最终消息中输出任何总结、表格、分类、类别名或分析。最多一句话确认完成。
前置条件
- Python 3.7+
- 四个固化脚本在
scripts/目录下:chunk_ebook.py— 电子书分段yaml_to_json.py— 将 subagent 输出的 YAML 转为标准 JSON(避免引号问题)manage_keywords.py— 管理already_searched.txt关键词去重列表(读取/追加)merge_to_sqlite.py— 合并 JSON 到 SQLite
详细工作步骤
步骤 0:创建/检查工作目录
检查 {书名}_tmp/ 目录是否存在。若不存在则创建:
mkdir -p "{书名}_tmp"步骤 1:检测断点(先于分片)
这是首要步骤。 每次启动必须先检测断点,根据已有数据决定后续走向。
1a. 检查是否已有分片文本
检查 {书名}_tmp/ 目录下是否已有 .txt 分片文件:
ls "{书名}_tmp/"*_001.txt 2>/dev/null && echo "分片已存在" || echo "需要分片"1b. 检查是否已有分片计划
检查 {书名}_tmp/_plan.json 是否存在。
1c. 检测已完成处理结果
若分片存在,扫描 {tmp_dir}/ 下以 {book_name}_ 开头、.json 结尾的文件:
ls "{tmp_dir}"/{book_name}_*.json 2>/dev/null | sed 's/.*_//' | sed 's/\.json//' | sort -n1d. 根据检测结果决定走向
| 情况 | 分片 txt 文件 | _plan.json | JSON 结果 | 处理方式 |
|---|---|---|---|---|
| 全新处理 | 不存在 | 不存在 | 不存在 | → 去步骤 2(分片),完成后继续步骤 4 |
| 部分完成 | 存在 | 存在 | 部分存在 | → 去步骤 4,从断点继续 |
| 全部完成 | 存在 | 存在 | 全部存在 | → 直接跳到步骤 5(合并) |
例如:
- 全新:
检测到无已有分片,将进行分片 → 共 202 个文本块待处理 - 断点:
检测到已有 15/42 块完成,将从第 16 块继续 - 全部完成:
检测到所有 42 块已完成,跳过提取直接合并
步骤 2:分段电子书(仅在无分片时执行)
若步骤 1 检测到无已有分片,使用固化脚本 scripts/chunk_ebook.py 将电子书分割为多个重叠的小文本块。
python scripts/chunk_ebook.py <电子书路径> --chunk-size 5000脚本会自动:
- 检测文件格式并提取全文
- 自动安装所需 Python 依赖
- 分割为约 5000 字符的文本块(块间重叠约 100 字符)
- 将文本块保存到
{书名}_tmp/{书名}_001.txt、{书名}_002.txt… 中 - 生成
_plan.json处理计划文件
务必使用此脚本执行分段,不要手动分段。
如果脚本执行成功,会输出类似:
[4/4] 写入处理计划: _plan.json
============================================================
完成! 所有文本块已保存到: 百年孤独_tmp
计划列表: 共 42 个文本块待处理
============================================================步骤 3(可选):初始化关键词去重列表
如果 {书名}_tmp/already_searched.txt 不存在,新建空文件:
touch "{书名}_tmp/already_searched.txt"文件由 manage_keywords.py filter 命令自动维护,无需手动编辑。
步骤 4:串行提取每个文本块的知识(subagent 隔离处理)
这是核心步骤。心态上要放慢:无论有 5 个块还是 500 个块,都一个接一个地处理,不急不躁。
我们最不缺的就是时间,最不需要的就是效率。提取质量远比速度重要。
设计思路
每个文本块的全部处理委托给一个独立的 subagent,主流程只负责串行调度和进度汇报。subagent 内部完成:
- 读取文本块 → 提取关键词(无 WebSearch)
- 运行
manage_keywords.py filter去重 - 对新关键词做 WebSearch + 撰写 YAML
- 运行
yaml_to_json.py转换
整个 pipeline 在 subagent 上下文中完成,主流程不接触每块的关键词文件、YAML、JSON 等中间产物细节,极大节省主上下文窗口。
处理流程
对 _plan.json 中的每个文本块(按 seq 升序),串行执行:
跳过已完成 — 若
{tmp_dir}/{book_name}_{seq}.json已存在,跳过不处理告知用户 —
[003/042] 正在处理 百年孤独_003.txt……启动 subagent 处理该块 — 使用 Agent 工具(prompt 见下方),等待其完成后再启动下一个
汇报进度(精简模式) — subagent 返回后,只报告一行:
[003/042] 百年孤独_003.txt → 5 个关键词,累计 23 个- 不要复述 subagent 输出的任何表格、分类、总结
- subagent 如果输出了多余内容,忽略即可,不转发给用户
若失败 — 重试该块,连续 3 次失败则跳过并在最终报告注明
subagent prompt
你是一个电子书知识提取助手。请完整处理一个文本块的知识提取 pipeline。
参数(替换实际值):
- 书名: {book_name}
- 文本块序号: {seq}/{total}
- 文本块文件: {chunk_filepath}
- 工作目录: {tmp_dir}
- 去重列表: {tmp_dir}/already_searched.txt
- 关键词输出文件: {tmp_dir}/keywords_{seq}.txt
- YAML 输出文件: {tmp_dir}/{book_name}_{seq}.yaml
=== 步骤 1:提取关键词 ===
使用 Read 工具阅读文本块文件,提取其中的**知识型条目**,将关键词列表写入 {tmp_dir}/keywords_{seq}.txt(使用 Write 工具)。
判断标准:**"一个不了解背景的读者,看到这个词会需要查资料才能理解吗?"**
- 需要 → 知识点,提取(如"扎染"、"傩戏"、"土楼")
- 不需要,仅作为叙事要素 → 不提取(如主角名"翠翠")
筛选类别(仅用于辅助判断,**禁止写入输出文件**):
- 物件·工艺 — 有民族/地方特色的物品、手工艺品、食物等(排除普通日用品)
- 习俗·仪式 — 有文化背景的风俗、节日、祭祀、禁忌
- 概念·观念 — 特有的思想、信仰、民间说法
- 事件·典故 — 有文化意义的历史事件、传说
- 人物 — 仅限真实历史人物或文化符号人物(排除书中叙事角色)
- 地点 — 仅限有历史文化意义的地点(排除虚构场景名)
关键约束 — 输出文件必须**纯词汇,不含任何类别标题**:
✅ 正确格式(只有词汇,一行一个):
扎染
傩戏
土楼
❌ 错误格式(禁止包含任何类别名、冒号、分隔线):
物件·工艺
扎染 ← 类别名不应出现
重要要求:
- 输出文件中**禁止出现任何类别名**,只能有纯词汇
- 无需分类、无需分组、无需标题、无需序号、无需分隔线
- 不要使用 WebSearch,不要写解释
- 按实提取,不设上下限
=== 步骤 2:去重过滤 ===
运行 Bash 命令对关键词去重:
```bash
python scripts/manage_keywords.py filter "{tmp_dir}/already_searched.txt" --from-file "{tmp_dir}/keywords_{seq}.txt" --output "{tmp_dir}/filtered_{seq}.txt"将输出(新关键词列表)保存到变量 NEW_KEYWORDS。
如果过滤后无新关键词(输出为空),必须将空 JSON 数组 [] 写入 {book_name}_{seq}.json 作为完成标记再结束,否则系统扫描不到该文件会认为该块未处理而重复执行:
echo '[]' > "{tmp_dir}/{book_name}_{seq}.json"写完后立即结束任务(跳过步骤 3 和 4),不要做任何多余操作。
=== 步骤 3:检索并撰写 YAML ===
对 NEW_KEYWORDS 中的每个关键词,使用 WebSearch 进行互联网检索,结合书中原文撰写综合解释。
- 用 Read 工具再次阅读文本块,找到每个关键词对应的原文句子
- 使用 WebSearch 检索每个关键词
- 输出 YAML 到 {tmp_dir}/{book_name}_{seq}.yaml
YAML 格式:
- 名词: White Walkers
解释: |
《冰与火之歌》中的神秘生物,又称异鬼,是长城以北的传说中存在。他们是一种寒冷而邪恶的人形生物,拥有将死者复活为尸鬼的能力。
书中原文: |
He had seen the white walkers in the woods beyond the Wall.
网络来源: |
https://iceandfire.fandom.com/wiki/White_WalkersYAML 书写规则:
- 每条记录以
- 名词:开头 - 字段缩进 2 空格
- 多行文本用
: |换行,内容缩进 4 空格 - 不需要引号
重要 — 语言要求:
名词字段:保留原文语言(原文是英文就保持英文,原文是日文就保持日文,以此类推)解释字段:始终使用中文撰写书中原文字段:保留书中原始文本,不做翻译网络来源字段:保留原始 URL
其他重要要求:
- 只处理 NEW_KEYWORDS 中的关键词
- "书中原文"必须是书中出现的完整可读的句子
- 检索不到则注明"未检索到网络资料"
- 禁止编造信息
=== 步骤 4:强制 YAML→JSON 转换 ===
python scripts/yaml_to_json.py "{tmp_dir}/{book_name}_{seq}.yaml"验证输出的 JSON 文件存在。若失败则重试步骤 3 和 4。
=== 【验收】步骤 5:字段合规检查 ===
用 Read 工具读取生成的 JSON 文件,逐条检查:
# 在 subagent 中用 Read 检查后,心理验证:
# 每个 dict 的 keys 必须严格等于 {"名词", "解释", "书中原文", "网络来源"}
# 不允许有第 5 个字段,不允许字段名错误,不允许空值如果任何条目存在以下问题,必须回到步骤 3 重新生成 YAML:
- 出现第 5 个额外字段
- 字段名错误(如"词条"代替"名词")
- 核心字段为空
- 格式不是有效的 YAML/JSON
验收通过后,只输出一句确认(例:"块 42/329 完成,3 个关键词"),禁止输出任何总结、表格、分类统计、类别名、markdown 表格、分隔线。拒绝输出表格式的任何内容。
#### 串行约束
- **必须串行,禁止并行** — 每次只启动一个 subagent,阻塞等待其完成后才启动下一个
- **慢就是快** — 每块可能需要数分钟,这是正常的
- subagent 是独立上下文,其内部细节不会污染主流程上下文窗口
- 若某块 subagent 连续失败 3 次,跳过该块,在最终报告中注明
### 步骤 5:合并到 SQLite
所有文本块处理完毕后,使用固化脚本 `scripts/merge_to_sqlite.py` 合并所有 JSON 到 SQLite 数据库。
```bash
python scripts/merge_to_sqlite.py <json_dir>
# 例如: python scripts/merge_to_sqlite.py 百年孤独_tmp其中 <json_dir> 就是步骤 0/2 中创建/使用的 {书名}_tmp/ 目录。
脚本会自动:
- 扫描目录下的所有
*.json文件(排除_开头的) - 按名词去重(不区分大小写),合并解释、原文和来源
- 写入 SQLite 数据库,文件名为
{书名}.db - 输出统计信息
脚本执行完成后,向用户汇报最终统计:
📊 处理完成统计
────────────────────────────────
总文本块数: 42
总原始条目: 587
去重后条目: 423
数据库文件: 百年孤独.db
────────────────────────────────输出说明
SQLite 数据库结构
库文件 {书名}.db 包含两个表:
nouns 表 — 知识条目:
| 列名 | 说明 |
|---|---|
| noun | 名词(主键,不区分大小写去重) |
| explanation | 综合解释 |
| original_text | 书中原文片段 |
| source_urls | 网络来源 URL |
| created_at | 创建时间 |
| updated_at | 更新时间 |
metadata 表 — 处理元信息:
| 列名 | 说明 |
|---|---|
| key | 元信息键名 |
| value | 元信息值 |
查询示例:
SELECT noun, length(explanation) as expl_len FROM nouns ORDER BY expl_len DESC LIMIT 10;
SELECT noun, source_urls FROM nouns WHERE source_urls != '' LIMIT 10;错误处理
subagent 处理失败
如果某个文本块的 subagent 处理失败(超时、格式错误等),重试该块而不是跳到下一个。如果连续 3 次失败,跳过该块并在最终报告中注明。重试时 already_searched.txt 中的关键词不会被重复搜索,不会浪费 token。
分段脚本失败
如果 chunk_ebook.py 失败,先检查文件格式是否支持。对于不支持的格式(如 MOBI 且没有 calibre),提示用户转换格式。
合并脚本失败
如果 merge_to_sqlite.py 失败,检查 JSON 文件是否完整。可以手动修复损坏的 JSON 后重试。
使用范例
用户说:"帮我提取《百年孤独》这本书的知识点"
你会回答好的,然后执行:
- 检查
百年孤独_tmp/目录是否存在,检测断点 → 判断是否为全新处理 - 若为全新,运行
python scripts/chunk_ebook.py 百年孤独.epub分片 - 确保
百年孤独_tmp/already_searched.txt存在 - 读取
_plan.json列出文本块数,告知用户预计时间 - 串行启动 subagent(从断点处开始),每个处理一个文本块的完整 pipeline
- 每块完成后简短汇报一次进度
- 全部完成后运行
python scripts/merge_to_sqlite.py 百年孤独_tmp - 展示最终统计