关于 local-paper-index
用于将本地 PDF 或 Markdown 文档构建为可搜索 Asta 文档索引的技能,面向整理和检索文档集合的用户。它提供提取、分块、写入 YAML 索引及预热搜索缓存的脚本,并支持断点续跑与相对路径。
主要功能
- 01将 PDF 转换为 Markdown,可选择提取图片
- 02按约 2000 字符分块并写入 YAML 索引
- 03支持直接索引现有 Markdown 文件
- 04使用相对路径并保留已有索引文档
- 05提供可恢复的处理流程和缓存预热脚本
适用场景
- 01为本地论文 PDF 集合建立语义搜索索引
- 02将 Markdown 笔记整理为可检索文档集合
- 03中断后继续 PDF 提取与索引任务
- 04按集合标签或来源 PDF 搜索索引内容
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ allenai/ asta-plugins --skill local-paper-index安装后在 Claude Code 中输入/local-paper-index确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/chunk-and-index.pyextract-pdfs.shwarm-cache.sh详细介绍
本地 PDF 索引构建器
从一组本地 PDF 文件构建可搜索的 Asta 文档索引。每个 PDF 都会转换为 Markdown,拆分为约 2000 个字符的文本块,并作为文档写入 asta-documents YAML 索引,从而支持对整个文档集的全文语义搜索。
资源
此技能在 assets/ 目录中包含独立脚本:
| 脚本 | 用途 |
|---|---|
assets/extract-pdfs.sh | 通过 asta pdf-extraction remote 将 PDF 转换为 Markdown |
assets/chunk-and-index.py | 拆分 Markdown 文件并直接写入 YAML 索引 |
assets/warm-cache.sh | 执行初次搜索以构建搜索缓存 |
根据此技能文件的位置查找资源目录。这些脚本是独立的,可以复制到工作目录中,也可以直接在原位置运行。
流程
步骤 0:询问用户的路径和文档集名称
开始之前,向用户询问以下四项:
- PDF 目录 — PDF 文件位于何处?
- Markdown 输出目录 — 提取出的 Markdown 应保存到何处?
- 文档集名称 — 为文档集取一个简短标签(例如
my-papers、cs-reading-list)。 - 是否包含图片? — 是否提取并保存 PDF 中嵌入的图片,并与 Markdown 放在一起。图片对包含图表/示意图的论文很有用,但会占用更多存储空间。默认:否。
建议采用以下目录布局:PDF 和 Markdown 位于同一父目录下的相邻目录中,索引文件与它们放在一起。例如,如果 PDF 位于 /data/papers/pdfs/:
/data/papers/ # DATASET_ROOT — parent of everything
├── pdfs/ # PDF_DIR (user already has this)
├── markdown/ # MARKDOWN_DIR (suggested: sibling of pdfs/)
│ ├── paper1.md # without --images: flat .md files
│ ├── paper2.md
│ ├── paper3/ # with --images: per-PDF subdirectories
│ │ ├── paper3.md
│ │ └── img-0.jpeg
│ └── ...
└── index.yaml # INDEX_PATH (auto-created here)
此布局很重要,因为索引会存储 Markdown 文件的相对路径。将所有内容放在同一个根目录下可使索引便于移植,也便于使用 Git 管理。
建议时的关键规则:
- Markdown 目录应与 PDF 目录相邻,而不是位于
.asta内。 DATASET_ROOT是同时包含PDF_DIR和MARKDOWN_DIR的父目录。- 索引位于
DATASET_ROOT/index.yaml。 - 如果用户的 PDF 位于
/home/user/research/pdfs,建议使用/home/user/research/markdown和/home/user/research/index.yaml。
用户确认(或提供自己的路径)后,设置以下变量:
PDF_DIR="/data/papers/pdfs" # user-provided
MARKDOWN_DIR="/data/papers/markdown" # user-confirmed
DATASET_ROOT="/data/papers" # parent of both
INDEX_PATH="$DATASET_ROOT/index.yaml" # derived
COLLECTION="my-papers" # user-chosen
IMAGES=false # true if user wants images
步骤 1:查找 PDF 并展示估算结果
PDF_COUNT=$(find "$PDF_DIR" -name "*.pdf" -type f | wc -l)
TOTAL_SIZE=$(find "$PDF_DIR" -name "*.pdf" -type f -exec du -ch {} + | tail -1 | awk '{print $1}')
echo "Found $PDF_COUNT PDFs ($TOTAL_SIZE total)"
继续操作前,向用户展示以下估算结果:
| 指标 | 估算 |
|---|---|
| 找到的 PDF | N 个文件 |
| 磁盘上的总大小 | X MB |
| 提取时间 | 每份 10 页 PDF 约需 2-5 分钟(远程 API);对于超过 20 份的批次,使用 olmocr 会更快 |
| 拆分与索引 | 每份 PDF 约需 1-2 秒 |
| 索引存储空间 | 提取文本大小的约 2-3 倍(Markdown 文件 + 含文本块内容的 YAML) |
| 缓存预热 | 5-30 秒(索引完成后只需执行一次) |
| 预计总时间 | 主要取决于提取:约为 N_papers x 3 分钟 |
开始前请征求用户确认,尤其是文档集较大时(超过 20 份 PDF)。
步骤 2:将 PDF 提取为 Markdown
# Without images (flat layout: markdown/paper.md)
bash /path/to/assets/extract-pdfs.sh "$PDF_DIR" "$MARKDOWN_DIR"
# With images (per-PDF subdirectories: markdown/paper/paper.md + images)
bash /path/to/assets/extract-pdfs.sh --images "$PDF_DIR" "$MARKDOWN_DIR"
只有在用户于步骤 0 选择包含图片时,才传入 --images。使用 --images 时,每个 PDF 都会在 MARKDOWN_DIR 下拥有自己的子目录,以避免不同 PDF 的图片文件名冲突。步骤 3 中的拆分脚本会自动处理这两种目录布局。
此脚本会:
- 跳过已存在 Markdown 文件的 PDF(可恢复运行)
- 将大型 PDF(超过 50 页)按每 50 页为一段进行提取
- 显示进度和数量
对于大型批次(超过 20 份 PDF),使用带有 --workers 的 asta pdf-extraction olmocr 会快得多。详情请参阅 pdf-extraction 技能。
步骤 3:分块并构建索引
uv run --with pyyaml python3 /path/to/assets/chunk-and-index.py "$COLLECTION" "$MARKDOWN_DIR" --index-path "$INDEX_PATH" --pdf-dir "$PDF_DIR"
--index-path 参数为必填项。该脚本会:
- 根据索引文件所在目录计算路径,并在
url字段中存储相对路径,使索引可在不同机器间移植。被索引的文档就是 Markdown 文件:其url指向.md文件。 - 读取每个 Markdown 文件,在段落或句子边界处分割成约 2000 字符的块
- 一次性将所有文档写入索引 YAML
- 保留索引中已有的文档(追加而非覆盖)
- 跳过此集合中已建立索引的 Markdown 文件(通过
url判断是否可恢复;可安全地重复运行) - 遍历
--pdf-dir,根据基本名称(每个 PDF 的子目录名称或扁平文件名去掉扩展名后的部分)查找每个.md对应的上游 PDF——它会查找磁盘上实际存在的 PDF,而非假设文件名。没有匹配 PDF 的.md会在没有source_pdf的情况下建立索引,并发出警告。 - 每个文档包含:
- 共享元数据(位于
extra中):collection,以及source_pdf(指向上游 PDF 的相对路径或file://路径),仅当提供了--pdf-dir且找到了匹配的 PDF 时才包含 - 每个块的元数据:
chunk_index、total_chunks、chunk_chars、chunk_offset、file_chars(位于extra中) - 标签:
<collection-name>,以及 PDF 转换的 Markdown 对应的pdf-index,或原始 Markdown 对应的md-index
- 共享元数据(位于
选项:
--chunk-size 2000— 调整分块大小(默认 2000 字符)--pdf-dir "$PDF_DIR"— 上游源 PDF 所在目录。索引自行编写的 Markdown 时请省略此参数(参见下文的索引原始 Markdown)。
步骤 4:预热搜索缓存
bash /path/to/assets/warm-cache.sh "$DATASET_ROOT"
此参数为必填项:
$DATASET_ROOT— 包含index.yaml的根目录
此步骤为必需步骤。 建立索引后,首次搜索会构建内部 BM25 和嵌入索引。如果不预热,用户的首次实际搜索会意外地变慢。
步骤 5:报告结果
asta documents --root "$DATASET_ROOT" list --tags="$COLLECTION"
asta documents --root "$DATASET_ROOT" show
告知用户:
- 已处理的 PDF 数量和创建的块数
- 数据集根目录:
DATASET_ROOT路径 - 索引位置:
INDEX_PATH - 用于筛选的集合标签:所选集合名称
- 搜索方法:
asta documents --root "$DATASET_ROOT" search --summary="query" --tags="COLLECTION"
搜索索引
构建完成后,可搜索所有已建立索引的 PDF:
# Semantic search within the collection
asta documents --root "$DATASET_ROOT" search --summary="neural network architecture" --tags="my-papers"
# With relevance scores
asta documents --root "$DATASET_ROOT" search --summary="attention mechanism" --tags="my-papers" --show-scores
# Filter by source PDF
asta documents --root "$DATASET_ROOT" search --extra=".source_pdf contains some-paper"
# List all documents in the collection
asta documents --root "$DATASET_ROOT" list --tags="my-papers"
索引原始 Markdown(无 PDF)
如果语料库已经是 Markdown(自行编写的 .md 文档、导出的笔记、调查记录、wiki),则无需提取内容——跳过步骤 1–2,直接将分块器指向 Markdown 目录。只需省略 --pdf-dir:
COLLECTION="my-notes"
MARKDOWN_DIR="/data/notes" # a tree of .md files (rglob, nested OK)
INDEX_PATH="/data/notes/index.yaml"
uv run --with pyyaml python3 /path/to/assets/chunk-and-index.py \
"$COLLECTION" "$MARKDOWN_DIR" --index-path "$INDEX_PATH"
bash /path/to/assets/warm-cache.sh "$(dirname "$INDEX_PATH")"
asta documents --root "$(dirname "$INDEX_PATH")" search \
--summary="your query" --tags="$COLLECTION" --show-scores
Markdown 就是源文件:每个文档的 url 都指向 .md 文件,次级标签为 md-index,且不存在 extra.source_pdf(因为没有上游 PDF)。分块、相对路径 URL 和断点续做行为与 PDF 流程完全相同——两者唯一的区别是是否存在 extra.source_pdf。
存储空间估算
| 集合大小 | 预计索引大小 | 预计 Markdown 大小 |
|---|---|---|
| 10 个 PDF(约 10 页/个) | 2-5 MB | 1-3 MB |
| 50 个 PDF(约 10 页/个) | 10-25 MB | 5-15 MB |
| 100 个 PDF(约 10 页/个) | 20-50 MB | 10-30 MB |
总存储空间约为提取文本的 2-3 倍(Markdown 文件 + summary 字段中包含块文本的索引 YAML)。
时间估算
| 阶段 | 每个 PDF | 备注 |
|---|---|---|
| 提取(远程) | 2-5 分钟/10 页 | 受 API 限制;每次调用最多 50 页 |
| 提取(olmocr) | 10-20 秒/页,并行处理 | 更适合超过 20 个 PDF 的情况 |
| 分块与建立索引 | 1-2 秒 | 一次性写入 YAML,速度快 |
| 预热缓存 | 总计 5-30 秒 | 建立索引后执行一次 |
重要说明
- 预热缓存。 首次运行
asta documents search --summary=...会构建搜索索引。索引完成后,务必运行缓存预热脚本。 - 分块大小的权衡。 2000 个字符可以兼顾搜索精度和上下文。分块越小,命中越精确,上下文越少;分块越大,上下文越多,但相关性会降低。
- 可恢复运行。 提取和索引都会跳过已处理的文件。中断后可安全地重新运行。
- 索引仅追加。 分块脚本会保留索引中的现有文档。若要从头重建,请先删除
index.yaml。 - 需要 PyYAML。 分块脚本需要
pyyaml。如果尚未安装,请运行pip install pyyaml或uv pip install pyyaml进行安装。 - 相对路径。 索引中存储的是相对路径(例如
markdown/paper.md),以便数据集可移植。这要求 Markdown 目录位于与索引文件相同的目录下。