Communitygithub.com

LPSlv/doc-extract

PDFなどのドキュメントから高速にテキストを抽出し、不足部分(図表、スキャンページ、構造化できなかったテーブル)だけをVison APIで補完するエージェントスキルです。

doc-extract とは?

このスキルは、AIエージェントがPDF、Word、Excel、PowerPoint、画像ファイルから正確かつ効率的に情報を取得するためのドキュメント抽出ツールです。まずローカルの高速テキスト抽出エンジンで大部分のテキストを取得し、それでも不足している部分(図表、スキャンページ、構造化に失敗したテーブル)のみをVison APIに送信することで、コストと速度を最適化します。Claude CodeやCursorなどの任意のエージェントプラットフォームで動作し、依存関係は`uv`のみで自動解決されます。具体的には、`convert.py`でファイルを解析し、抽出できなかったアイテムを`pending`リストとして返します。エージェントはそのアイテムを順に`describe.py`で説明するだけで完了し、説明内容は元のテキストと区別可能な形で`doc.md`に追記されます。また、スケールガード機構により、大量のVison呼び出しが必要な場合はユーザーの確認を得てから実行するなど、安全設計も備えています。

対応✓Claude Code~Codex CLI~Cursor
npx skills add LPSlv/doc-extract

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

doc-extract は何をしますか?

テキスト抽出は、ほとんどのドキュメントを単独で処理します。このスキルは、そのために高速なローカルエンジンを使用し、証明可能な不足部分(図、スキャンページ、構造化に失敗したテーブル)に対してのみVison呼び出しを行います。

.pdf、.docx、.xlsx、.pptx、および画像ファイルを読み取ります。形式は拡張子ではなく、内容によって決定されます。

使用すべきでない場合

  • マージ、分割、フォーム入力、暗号化 → anthropics/skills@pdf
  • 1つの数値が必要で、そのページが既にわかっているドキュメント → 単にその部分だけ読み取ってください。

uvが必要です。それ以外は不要です。依存関係は初回実行時に解決されます。

1. 変換

uv run <skill-dir>/convert.py FILE [MORE ...]
uv run <skill-dir>/convert.py FILE --inline    # 説明を画像の位置に配置

1つのドキュメントにつき1つのJSONオブジェクトが標準出力に出力されます。決定論的な処理はすべてここで完了します。テキスト抽出、画像書き出し、アーティファクトのキャッシュが行われます。いずれかのドキュメントで失敗した場合、終了コードは0以外になります。

{"status":"ok","artifact":"/home/…/<sha>-<tag>","cached":false,
 "doc_md":"…/doc.md","pages_dir":"…/pages","manifest":"…/manifest.json",
 "pending":[{"id":"p007-render","page":7,"kind":"page_render",
             "reason":"dense_grid","path":"…/images/p007-render.png"}],
 "dropped":6,"over_scale_guard":false,"scale_guard":15}

最初にstatusを確認してください。encrypted、unreadable、unsupportedは終端ステータスです。報告して次のファイルに進んでください。バッチ処理は続行されます。同じファイルを再実行すると、即座にcached: trueが返され、コストはかかりません。

over_scale_guardがtrueの場合、必要な呼び出し回数をユーザーに伝え、続行する前に同意を得てください。84ページのスキャンは84回の呼び出し、40枚の画像スライドデッキも同様です。

2. 各保留アイテムを確認する

pendingが空の場合 → 完了です。ステップ3に進んでください。

空でない場合、各エントリについて、pathにあるファイルを読み取り、見た内容を書き込みます。

uv run <skill-dir>/describe.py <artifact> <id> "説明文"
# または長い説明をパイプで渡す: … describe.py <artifact> <id> -

説明の書き方はreasonによって異なります。詳細はreference/describing-visuals.mdを参照してください。

reason書くべき内容
standalone_raster、curves、diagonals図を説明:種類、何を示しているか、軸と単位、注目すべき値、読み取れるすべてのテキスト。
no_text_layerページを逐語的に書き写す。 これはOCRパスです。テキストがまったくありません。
dense_grid、stroke_grid抽出機が構造化できなかったテーブル。Markdownテーブルとして再現する。
raster_grid多くの画像が並べられたページ(合成図、または写真がストリップ状に保存されたもの)。パネルが全体として何を示しているかを説明し、その後、パネルごとに注目すべき点を説明する。

describe.pyは再実行しても安全です。重複ではなく置換するため、途中でVisionパスが失敗しても再開できます。

kind: "native_chart"のアイテムはpendingに現れません。これらはスプレッドシートのチャートであり、系列がチャート定義から読み取られているため、数値はピクセルからの推定ではなく正確です。

3. 回答

  • ドキュメント全体:doc.md。
  • 特定の質問:ユニットファイルをgrepして、必要なものだけを読み取ってください。
  • ユニットで引用するか、複数のドキュメントにわたってファイル名をプレフィックスとして付けます([report.pdf:p12])。
形式ユニット引用例ユニットファイル
PDFページ[p12]pages/p012.md
PowerPointスライド[s07]pages/u007.md
Excelシート[Sheet2]pages/u002.md
Wordレベル1見出し[予算の前提]pages/u003.md
画像ファイル全体[img]pages/u001.md

Officeユニットファイルは名前ではなく番号で識別されます。これは、Q1 P&L / draftというシート名がファイル名として適切でないためです。manifest.jsonには、各ファイルをラベルにマッピングするunitsリストが順序付きで含まれています。

唯一のルール

決してdoc.mdを手動で編集しないでください。 追加するものはすべてdescribe.pyを通じて行い、区切り文字でラップされます。これにより、ベンチマークが追加部分を取り除き、スキルがテキスト抽出を劣化させていないことを証明できます。手動で編集すると、その保証が静かに破壊されます。

説明の配置場所と、--inlineが保証しないこと

デフォルトでは、すべての説明はdoc.mdの末尾に1つのブロックとして追加され、[s02]、[p12]、[Sheet2]のようにラベル付けされます。--inlineは代わりに、各ブロックを画像の元の位置に配置します。これを変換時に渡し、describe.pyには渡さないでください。配置はアーティファクト(およびそのキャッシュキー)に記録されるため、再開されたVisionパスが2つの方式を混在させることはありません。

形式--inlineがアンカーできる対象
PowerPoint画像の行が確実にその画像のものである場合、その行。それ以外はスライドの末尾。
Word画像が存在するセクションの末尾。anydocは画像をaltテキストとしてレンダリングしますが、Wordは作成者が入力しない限りaltテキストを書き出さないため、通常はアンカーとなる行がありません。
Excelシートの末尾。画像とチャートはパッケージから取得され、anydocはどちらもレンダリングしません。
PDF、画像なし。--inlineはPDFに対して何も変更しません。

ブロックはエンジンの行の隣に挿入され、置換されることはありません。これがバイト単位の同一性が維持される理由です。eval/gate.pyは両方の配置を実行し、インプレース編集があった場合は失敗します。

インラインが保証しないことは、位置が正確であることです。バイト単位の同一性は配置をチェックできません。なぜなら、挿入はどこに配置されてもラウンドトリップするからです。アンカーはanydocのaltテキストであり、これは通常の散文です。image.pngというファイルに関するスライドは、画像と同じ行をレンダリングします。したがって、画像の専用行は、そのユニット内の候補行数がそのユニット内の画像数と等しい場合にのみ使用され、それ以外の場合はユニット境界にフォールバックします。位置は、その画像のものであることが証明されているか、ユニット境界のいずれかです。見た目が似ている2行の間で推測することはありません。

フィルタがこのようになっている理由

これらを「簡略化」しないでください。それぞれが存在する理由は、明白な代替案が実際のドキュメントでテストされ、失敗したからです。

フィルタ理由
ユニットの50%以上、<120px、アスペクト比>8:1の画像を除去14ページの助成金申請PDFに69個の画像配置があった。7つの異なるオブジェクトのうち、6つはロゴ、罫線、サイドバーの帯だった。
すでにMarkdownテーブルを生成したページをスキップ抽出機が解析できるテーブルは、Visionよりも優れている。何も生成されなかった場合にのみ介入する。
両方の方向にストロークが必要下線と罫線は水平方向のみ。すべての軸方向ストロークをカウントすると、参考文献ページや見出しに下線のある契約書で誤検出が発生した。
diagonalsを面積の下限から除外4つ以上の斜めセグメントは本文テキストには出現せず、面積の下限がページ隅に配置されたチャートを拒否していた。
高密度グリッド分岐にインクしきい値抽出機が見逃した網掛けテーブルを、装飾的なセクションバナーから区別する。
6つ以上のラスターを持つページを1つのレンダリングにまとめる48タイルのインペインティング比較は1つの図であり、1つのTIパッケージ写真が12のストリップとして到着する。タイルごとの呼び出しはトークンコストが約2倍になり、構成が失われる。5〜6ラスターのページは別々の図であることが多いため、しきい値は6に設定されている。
EMF、WMF、OLEペイロードを除去テキストエンジンによって忠実に保持されるが、ラスタライザなしでは読み取り不可能。pendingにルーティングすると、エージェントが完了できないアイテムが作成される。droppedにカウントされ、静かに破棄されることはない。

harvest.pyはPDFルーティングの唯一の信頼できる情報源であり、filters.pyはすべての形式に適用される部分の信頼できる情報源です。READMEと設計仕様のすべての数値はこれらから再生成されています。数値を手動で編集しないでください。ファイルを実行できない場合は、reference/harvest-block.mdがコピー&ペースト可能な自己完結型のバックアップです。

Officeルーティングは意図的に薄く、それは正直な設計です

PDFページは描画コマンドのプログラムであり、図を隠します。そのためharvest.pyはベクトルジオメトリから図を推測します。OOXMLはパッケージ内で画像を宣言するため、推測するものは何もありません。Officeドキュメントには、ファニチャーフィルター、引用とキャッシュの契約、およびこのルーブリックが適用されます。ただし、レンダリングエンジンがないため、render_reason、raster_grid、cost_guardはありません。

関連スキル