Communitygithub.com

LPSlv/doc-extract

Compétence d'agent pour lire les PDF correctement : extraction locale rapide du texte, plus vision uniquement sur ce qu'elle a vraiment manqué — graphiques, pages scannées et tableaux non structurés.

Qu'est-ce que doc-extract ?

Cette compétence d'agent permet une lecture optimale des documents complexes (PDF, DOCX, XLSX, PPTX et images) en combinant une extraction de texte locale et rapide avec des appels vision ciblés uniquement sur les éléments que l'extraction automatique n'a pas su traiter : figures, pages sans couche texte, tableaux non structurés et grilles. Elle suit un pipeline en trois étapes : conversion du document (qui produit un artefact avec texte et images extraits), puis analyse des éléments en attente via `describe.py` qui remplace chaque image manquante par une description textuelle ou une transcription, et enfin réponse à l'utilisateur en citant des unités (page, slide, feuille, section). La compétence dispose de mécanismes de sécurité comme un garde-fou d'échelle pour éviter les coûts surprises sur les documents longs, et elle garantit l'intégrité du document original en ne modifiant jamais `doc.md` directement. Compatible avec tout outil d'agent utilisant `uv`, elle supporte les formats de bureautique et images avec un routage adapté (les PDF nécessitent une inférence vectorielle tandis que les formats OOXML déclarent leurs images explicitement).

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add LPSlv/doc-extract

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Que fait doc-extract ?

L'extraction de texte gère la plupart des documents toute seule. Cette compétence utilise des moteurs locaux rapides pour cela, puis consacre des appels vision uniquement sur ce qu'elle a vraiment manqué — figures, pages scannées et tableaux qu'elle n'a pas réussi à structurer.

Lit les fichiers .pdf, .docx, .xlsx, .pptx, et les images. Le format est déterminé par le contenu, pas par l'extension.

Quand ne PAS utiliser cela

  • Fusionner, fractionner, remplir des formulaires, crypter → anthropics/skills@pdf.
  • Un document dont vous avez besoin d'un seul numéro et dont vous connaissez déjà la page → lisez-le simplement.

Nécessite uv. Rien d'autre ; les dépendances se résolvent au premier lancement.

1. Convertir

uv run <skill-dir>/convert.py FICHIER [AUTRE ...]
uv run <skill-dir>/convert.py FICHIER --inline    # descriptions à la position de l'image

Un objet JSON par document, sur stdout. Tout ce qui est déterministe est maintenant fait : texte extrait, images écrites, artefact mis en cache. Le code de sortie est non nul si un document a échoué.

{"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}

Vérifiez d'abord status. encrypted, unreadable et unsupported sont terminaux — signalez et passez ; le lot continue. Relancer le même fichier retourne cached: true instantanément et ne coûte rien.

Si over_scale_guard est vrai, dites à l'utilisateur combien d'appels cela nécessite et obtenez son accord avant de continuer. Un scan de 84 pages représente 84 appels, et un diaporama de 40 images également.

2. Regarder chaque élément en attente

pending est vide → vous avez terminé, passez à l'étape 3.

Sinon, pour chaque entrée : lisez le fichier à path, puis écrivez ce que vous avez vu :

uv run <skill-dir>/describe.py <artifact> <id> "votre description"
# ou pipez une longue :  … describe.py <artifact> <id> -

Comment écrire la description dépend de reason — voir reference/describing-visuals.md :

reasonQue écrire
standalone_raster, curves, diagonalsDécrivez la figure : type, ce qu'elle montre, axes et unités, valeurs notables, tout texte lisible.
no_text_layerTranscrivez la page textuellement. C'est le chemin OCR ; il n'y a aucun texte du tout.
dense_grid, stroke_gridUn tableau que l'extracteur n'a pas réussi à structurer. Reproduisez-le sous forme de tableau Markdown.
raster_gridUne page carrelée avec de nombreuses images — une figure composite, ou une photo stockée sous forme de bandes. Décrivez ce que les panneaux montrent ensemble, puis tout élément notable par panneau.

describe.py est sûr à réexécuter — il remplace plutôt que duplique, donc un passage vision qui s'arrête à mi-chemin peut simplement être repris.

Les éléments avec kind: "native_chart" n'apparaissent jamais dans pending. Ce sont des graphiques de tableur dont les séries ont été lues à partir de la définition du graphique, donc leurs nombres sont exacts plutôt qu'estimés à partir des pixels.

3. Répondre

  • Document entier : doc.md.
  • Une question spécifique : grep les fichiers par unité et lisez seulement ceux dont vous avez besoin.
  • Citez par unité, ou préfixez le nom du fichier dans plusieurs documents ([report.pdf:p12]).
FormatUnitéCiter commeFichier par unité
PDFpage[p12]pages/p012.md
PowerPointdiapositive[s07]pages/u007.md
Excelfeuille[Sheet2]pages/u002.md
Wordtitre de niveau 1[Budget assumptions]pages/u003.md
imagefichier entier[img]pages/u001.md

Les fichiers d'unité Office sont numérotés plutôt que nommés, car une feuille appelée Q1 P&L / draft n'est pas un nom de fichier. manifest.json contient une liste units mappant chaque fichier à son libellé, dans l'ordre.

La règle unique

Ne modifiez jamais doc.md à la main. Tout ce que vous ajoutez passe par describe.py, qui l'entoure de délimiteurs. C'est ce qui permet au benchmark de supprimer les ajouts et de prouver que la compétence ne dégrade pas l'extraction de texte. Éditer sur place brise cette garantie silencieusement.

Où les descriptions atterrissent, et la seule chose que --inline ne promet pas

Par défaut, chaque description est ajoutée en un seul bloc à la fin de doc.md, étiquetée [s02], [p12] ou [Sheet2]. --inline place plutôt chaque bloc à la position de son image. Passez-le au moment de la conversion, pas à describe.py : le placement est enregistré dans l'artefact (et dans sa clé de cache), donc un passage vision repris ne peut pas mélanger les deux.

FormatÀ quoi --inline peut s'ancrer
PowerPointla ligne de l'image elle-même, lorsque cette ligne est prouvée être celle de l'image ; sinon la fin de la diapositive
Wordla fin de la section dans laquelle se trouve l'image — anydoc rend une image comme son texte alternatif, et Word n'en écrit aucun à moins que l'auteur n'en ait saisi un, donc il n'y a généralement pas de ligne à laquelle s'ancrer
Excella fin de la feuille — les images et graphiques proviennent du package, et anydoc ne rend ni les uns ni les autres
PDF, imagerien. --inline ne change rien pour un PDF

Le bloc est inséré à côté de la ligne du moteur, jamais substitué à celle-ci, c'est pourquoi l'identité octet survive — eval/gate.py exécute les deux placements et échoue sur toute modification sur place.

Ce que inline ne promet pas, c'est que la position est correcte ; l'identité octet ne peut pas vérifier le placement, car une insertion est round-trip peu importe où elle atterrit. L'ancre est le texte alternatif d'anydoc, qui est de la prose ordinaire : une diapositive sur un fichier appelé image.png rend la même ligne qu'une image. Donc la ligne propre de l'image est utilisée seulement lorsque le nombre de lignes candidates dans cette unité est égal au nombre d'images qu'elle contient, et la description retombe sur la limite d'unité autrement. Les positions sont soit prouvées être celle de cette image, soit une limite d'unité — jamais une supposition entre deux lignes qui se ressemblent.

Pourquoi les filtres sont ce qu'ils sont

Ne les "simplifiez" pas — chacun existe parce que l'alternative évidente a été testée et a échoué sur des documents réels :

FiltrePourquoi
Ignorer les images sur >50% des unités, <120px, rapport d'aspect >8:1Un PDF de subvention de 14 pages avait 69 placements d'images : 7 objets distincts, dont six logos, règles et une barre latérale.
Ignorer les pages qui ont déjà produit un tableau MarkdownL'extracteur est meilleur sur les tableaux qu'il peut analyser que la vision. N'intervenez que là où il n'a rien produit.
Nécessiter des traits dans les deux orientationsLes soulignés et les règles sont uniquement horizontaux. Compter tous les traits alignés sur les axes a déclenché des alertes sur les pages de bibliographie et les contrats avec des titres soulignés.
Exempter diagonals du seuil de surface4 segments diagonaux ou plus n'apparaissent pas dans le corps du texte, et le seuil rejetait les graphiques placés dans un coin de page.
Seuil d'encre sur la branche des grilles densesSépare un tableau ombré que l'extracteur a manqué des bandeaux décoratifs de section.
Réduire les pages avec >6 rasters en un seul renduUne comparaison d'inpainting de 48 tuiles est une seule figure, et une photo de package TI arrive sous forme de 12 bandes ; les appels par tuile coûtent ~2× les tokens et perdent la composition. Les pages avec 5–6 rasters sont parfois des figures distinctes, donc la limite est à 6.
Ignorer les charges utiles EMF, WMF et OLERetenues fidèlement par le moteur de texte, et illisibles sans un rastériseur — les diriger vers pending créerait un élément qu'aucun agent ne peut compléter. Comptées dans dropped, jamais ignorées silencieusement.

harvest.py est la source unique de vérité pour le routage PDF, et filters.py pour les parties qui s'appliquent à tous les formats. Chaque nombre dans le README et la spécification de conception est regénéré à partir d'eux ; ne modifiez jamais un nombre manuellement. Si vous ne pouvez pas exécuter un fichier, reference/harvest-block.md est une copie autonome à coller et exécuter.

Le routage Office est délibérément plus léger, et c'est honnête

Une page PDF est un programme de commandes de dessin qui cache ses figures, c'est pourquoi harvest.py les déduit de la géométrie vectorielle. OOXML déclare ses images dans le package, donc il n'y a rien à déduire. Les documents Office reçoivent les filtres de mobilier, le contrat de citation et de cache, et cette rubrique — mais pas de render_reason, pas de raster_grid et pas de cost_guard, car sans moteur de rendu il n'y a ni diapositive ni feuille à rendre.

Skills associés