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 :
reason | Que écrire |
|---|---|
standalone_raster, curves, diagonals | Décrivez la figure : type, ce qu'elle montre, axes et unités, valeurs notables, tout texte lisible. |
no_text_layer | Transcrivez la page textuellement. C'est le chemin OCR ; il n'y a aucun texte du tout. |
dense_grid, stroke_grid | Un tableau que l'extracteur n'a pas réussi à structurer. Reproduisez-le sous forme de tableau Markdown. |
raster_grid | Une 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]).
| Format | Unité | Citer comme | Fichier par unité |
|---|---|---|---|
| page | [p12] | pages/p012.md | |
| PowerPoint | diapositive | [s07] | pages/u007.md |
| Excel | feuille | [Sheet2] | pages/u002.md |
| Word | titre de niveau 1 | [Budget assumptions] | pages/u003.md |
| image | fichier 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 |
|---|---|
| PowerPoint | la ligne de l'image elle-même, lorsque cette ligne est prouvée être celle de l'image ; sinon la fin de la diapositive |
| Word | la 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 |
| Excel | la fin de la feuille — les images et graphiques proviennent du package, et anydoc ne rend ni les uns ni les autres |
| PDF, image | rien. --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 :
| Filtre | Pourquoi |
|---|---|
| Ignorer les images sur >50% des unités, <120px, rapport d'aspect >8:1 | Un 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 Markdown | L'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 orientations | Les 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 surface | 4 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 denses | Sé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 rendu | Une 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 OLE | Retenues 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.